npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@chrischall/iaqualink-mcp

v0.2.2

Published

iAqualink (Zodiac/Fluidra) pool equipment MCP server for Claude — status plus confirm-gated pump, heater, light, aux and set-point control. Developed and maintained by AI (Claude Code).

Readme

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 |

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

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