Back to Directory/Developer Tools

io.github.chrischall/iaqualink-mcp

iAqualink pool equipment: status plus confirm-gated pump, heater, light and set-point control.

Developer ToolsTypeScriptv0.2.2

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: built from the open-source client, live verification pending. Every request shape is transcribed from iaqualink-py 0.7.0 (what Home Assistant uses) — see docs/IAQUALINK-API.md — and has not yet been exercised against a real pool. The login endpoint and app key are confirmed live.

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

Tools

ToolWhat it doesAnnotation
iaqualink_list_systemsSystems on the account and their serialsread
iaqualink_get_statusTemperatures, set points, pumps, heaters, aux/lights, chlorinator, salinity/pH/ORP (view: compact · full · raw)read
iaqualink_healthcheckWhether a sign-in is configured and acceptedread
iaqualink_set_equipmentPool/spa pump, pool/spa/solar heater; eXO filter pump, production, boost, low, heating — on/offwrite, not destructive
iaqualink_set_auxAux circuit or light on/off (by number, aux_<n> or label)write, not destructive
iaqualink_set_lightDimmable brightness (0/25/50/75/100) or color effect (iAqua)write, not destructive
iaqualink_set_temperaturePool and/or spa set point (iAqua °F/°C; eXO °C pool)write, not destructive
iaqualink_set_chlorinatoreXO chlorinator output % — inferred shape, not in iaqualink-pywrite, not destructive
iaqualink_list_schedulesRS panel programs via WebTouch (iAqua; iQ20 fallback probe); eXO timersread
iaqualink_add_scheduleAdd an RS panel program (circuit, start, stop, run days) via WebTouchwrite, not destructive
iaqualink_edit_scheduleChange an RS panel program's start / stop / run days via WebTouchwrite, not destructive
iaqualink_delete_scheduleDelete an RS panel program via WebTouch — no undowrite, destructive
iaqualink_set_scheduleSet / re-time / disable an eXO timer (start–stop, rpm on variable-speed pumps) — eXO only, unverified livewrite, not destructive
iaqualink_check_panelIs 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 disagreeread
iaqualink_list_pump_speedsRS panel variable-speed pump: the named speeds, their RPM and which one runs, via WebTouchread
iaqualink_set_pump_speedRun the pump at a named speed via WebTouchwrite, not destructive
iaqualink_set_pump_speed_rpmChange a named speed's RPM (600–3450) via WebTouch; a speed that is not running runs briefly while set, then the running speed is restoredwrite, not destructive
iaqualink_list_onetouchRS panel OneTouch scenes (Spa Mode, All OFF, …) and their state, via WebTouchread
iaqualink_set_onetouchTurn a OneTouch scene on/off via WebTouch — toggle not exercised live yetwrite, not destructive

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).

Setup

npm i -g @chrischall/iaqualink-mcp
VariableRequiredMeaning
IAQUALINK_EMAILfor every toolYour iAqualink app sign-in email
IAQUALINK_PASSWORDfor every toolYour iAqualink password (not trimmed)
IAQUALINK_STATUS_CACHE_SECONDSnoSeconds a status read is reused (default 5; 0 disables)
IAQUALINK_WEBTOUCH_STREAM_HOSTSnoExtra hosts (comma-separated) the WebTouch display stream may come from; default only prm.iaqualink.net
MCP_CONFIRM_MODEnoask-user (default) / auto / refuse
MCP_CONFIRM_TTL_SECONDSnoconfirmToken lifetime (default 600)
MCP_CONFIRM_ELICITATIONnooff 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 (derived from iaqualink-py; live verification pending).

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.

Installation

Source-derived launch command. Check the maintainer’s required arguments and credentials before running:

bash
npx -y @chrischall/iaqualink-mcp

Set up in your AI client

Merge this template into ~/Library/Application Support/Claude/claude_desktop_config.json. Keep existing servers. Add any arguments, credentials, and permissions required by the maintainer; this template has not been install-tested.

json
{
  "mcpServers": {
    "io-github-chrischall-iaqualink-mcp": {
      "command": "npx",
      "args": [
        "-y",
        "@chrischall/iaqualink-mcp"
      ]
    }
  }
}

Restart Claude Desktop completely for changes to take effect. Confirm the server appears connected in the client’s tool list, then try a read-only example from its documentation.

Claude Desktop setup reference

Package

@chrischall/iaqualink-mcpnpm

Compatible MCP Clients

io.github.chrischall/iaqualink-mcp works with any MCP-compatible client. Copy the config snippet from the Configuration section above and add it to the file shown for your client, then restart the application.

  • Claude Desktop~/Library/Application Support/Claude/claude_desktop_config.jsonRestart Claude Desktop completely for changes to take effect.
  • Cursor~/.cursor/mcp.jsonRestart Cursor for changes to take effect.
  • VS Code.vscode/mcp.jsonReload VS Code window for changes to take effect.
  • Windsurf~/.codeium/windsurf/mcp_config.jsonRestart Windsurf for changes to take effect.
  • Claude Code.mcp.jsonSave at the project root, then start Claude Code in that project and review the MCP server approval prompt. Keep real credentials out of shared files.

Learn More