MCP server: One MCP server.

iAqualink

@chrischallMCP server · External

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

v0.1.0MITTypeScript
Freeno charge per call

Runs on your machine from its package; the steps are below.

What it does

  1. Reads
  2. Writes
  3. High impact

At most: creates or changes data: records, files, settings.

What it costs

Free

Odel charges nothing per call. The publisher may charge for its own service.

Runs locally over stdio

This server isn't hosted — your MCP client launches it from a package registry. Use one of the commands below, or drop the config into your client (e.g. Claude Desktop).

npm@chrischall/iaqualink-mcpv0.1.0

Run

npx -y @chrischall/iaqualink-mcp

MCP client config

{
  "mcpServers": {
    "iaqualink": {
      "command": "npx",
      "args": [
        "-y",
        "@chrischall/iaqualink-mcp"
      ]
    }
  }
}
README3 min read

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

Every write has an inverse in this set and reaches no other person, so none is marked destructive.

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

sh
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)
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 and r-api.iaqualink.net, and 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

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

Similar modules