MCP server for AC Infinity grow controllers. Monitor, automate, and control your grow environment through natural conversation with Claude.
This file documents two categories of accepted risk:
Current posture: pip-audit runs with no --ignore-vuln flags at all. Every CVE
below is RESOLVED — each was fixed by a version pin rather than permanently suppressed.
If a new CVE appears in a pip-audit run, do not blanket-add it here — file an
issue, evaluate the exposure, and only ignore after a documented finding.
Base URL: https://www.acinfinityserver.com/api
The AC Infinity cloud API supports HTTPS. TLS handshake verified 2026-05-29: TLSv1.3, DigiCert Encryption Everywhere DV TLS CA certificate, valid until 2026-11-18. Credentials and session tokens are encrypted in transit. This supersedes the previous HTTP-only accepted risk documented before 2026-05-29.
All endpoints below are confirmed over HTTPS. This list was updated via network capture (Phase 17, 2026-05-22) to include all confirmed v2.0 endpoints.
| Endpoint | Purpose |
|---|---|
POST /user/appUserLogin |
Authentication — credentials encrypted via TLS |
POST /user/devInfoListAll |
Device list — response includes user email (appEmail) |
POST /log/dataPage |
Historical sensor and port data |
POST /dev/getdevModeSettingList |
Read current port mode settings |
POST /dev/addDevMode |
Write port mode settings |
POST /api/dev/getDevSetting |
Richer port settings (sensor calibration, load type, Matter/UUID fields) |
POST /api/upgrade/getUpgrade |
Firmware upgrade check |
POST /api/upgrade/downgrade |
Firmware downgrade info (returns download URL and release notes) |
| Endpoint | Purpose |
|---|---|
POST /api/version=2.0/dev/getGroups |
List all automation groups for a device |
POST /api/version=2.0/dev/addGroups |
Create automation group |
POST /api/version=2.0/dev/updateGroupsById |
Edit an automation rule in place by advId (read-before-write) |
POST /api/version=2.0/dev/updateGroupsIsOn |
Toggle automation on/off state |
POST /api/version=2.0/dev/delByid |
Delete automation |
| Endpoint | Purpose |
|---|---|
POST /api/version=2.0/dev/getAlarms |
List all alarm configurations for a device |
POST /api/version=2.0/dev/addAlarms |
Create alarm |
POST /api/version=2.0/dev/updateAlarmsById |
Enable, disable, or edit alarm |
POST /api/version=2.0/dev/delAlarmsByid |
Delete alarm |
| Endpoint | Purpose |
|---|---|
POST /api/log/logdataByAll |
Historical readings (alternative to /log/dataPage; confirmed working) |
DELETE /api/log/log?devId=...&time=... |
Delete all history logs for a device |
GET /api/version=2.0/dev/recipe?advVersion=1 |
Grow stage templates (Seedling, Vegetative, Flowering, Plant Kit, Drying) |
AC Infinity permits only one active session per account. Authenticating through this server (initial login or a transparent session-expiry re-auth) can invalidate the user’s AC Infinity mobile-app session, and vice versa. This is an upstream account-model constraint, not a defect in this server.
10003) triggers transparent re-auth and
retry on reads only. On writes it is surfaced as an API error and never replayed,
because the write may have been processed server-side before the expiry response — a silent
retry could double-apply state. A refresh-failure cache bounds re-login to one attempt so a
bad credential does not hammer the login endpoint. See docs/API.md Quirk 31.discover_devices tool docstring carries a grower-readable heads-up.The client sends AC-app-style User-Agent headers rather than the default
python-requests UA, so traffic is indistinguishable from the official mobile app:
| Endpoint class | User-Agent |
|---|---|
Login (/user/appUserLogin) |
ACController/1.8.2 (com.acinfinity.humiture; build:489; iOS 16.5.1) |
| Data / write endpoints | okhttp/3.10.0 |
These are spoofed identity strings sent to the upstream API; they contain no user data and no credentials. A regression test locks both values. This is an accepted-behavior note, not a vulnerability.
The AI+ write path (devType >= 20) sends one additional spoofed header,
minversion: 3.5, and no extra User-Agent — the table above is complete.
It is sent on addDevMode and on three of the five v2 Groups endpoints, not on
every request.
That header is an opaque server-side gate rather than an app identity, and only
the literal string "3.5" is accepted. Without it, three distinct things happen
depending on the endpoint: addDevMode, updateGroupsIsOn and delByid return
100001; addGroups returns nothing at all and the caller dies on a 10s read
timeout; getGroups and updateGroupsById are unaffected and are therefore not
sent the header. See docs/API.md Quirk 14 for the v1 ablation and Quirk 39
for the per-endpoint v2 measurement. Three further app-identity headers
were tested and proven unnecessary, so they are deliberately not sent — every
declared header is surface for the kind of upstream tightening that broke the v2
endpoints in #298. The exact value is locked by a regression test mirroring the
#251 lock.
currentMode fix (#326, #328)The Advance Automation (Groups) currentMode enum is device-class dependent: legacy
(devType 11/18) and new-framework (devType ≥ 20) controllers number the same five modes
differently, and 1/2 are inverted between them. Before the fix, a mode="off"
automation written to a new-framework controller emitted the legacy 2, which that class
reads as On — an automation created to hold a grow light off ran it at full power. The
same wrong table read backwards also made the read-before-write edit path overlay
cycle timings onto a temperature automation. Both directions are fixed (see docs/API.md
Quirk 35), and no new endpoint or input surface was added. Three residuals are
documented, not mitigated:
on is the only mode observed on both
new-framework device types; auto and vpd are evidenced on devType 20 only, and are
read-confirmed rather than write-confirmed. Writing either mode on a devType 22 is
therefore a first-time write against an unverified integer.currentMode (three
scalars and a validated mode string — never the payload dict, which carries device
identifiers). This exists because #326 had to be diagnosed on live hardware by a human
watching a light fixture; it makes a future mis-write attributable, it does not stop one.Unchanged and verified preserved by this fix: the ADVANCE pre-write guard (Quirk 25), the
1.5 s inter-write rate limit, dry_run=True on every write tool, and the
ConnectionError-only retry policy on writes.
mcp package — RESOLVED (Issue #313, 2026-08-28)mcp (Model Context Protocol Python SDK; a direct dependency)mcp SDK’s
transport handling for code paths this server does not use. Our server runs stdio
transport only (see server.py:main()); the affected paths require HTTP/SSE
transport. The exposure was theoretical throughout.--ignore-vuln flag plus a 2026-08-22 re-evaluation date. P3-C2-F006
later noted that a fresh install showed no findings under current pins, and set the
retirement test: run pip-audit without the flag in a clean venv: if the CVE does
not appear, the patched version is already pulled in and the ignore can be removed.mcp>=1.14.1,<2 (the unbounded floor was resolving to 2.x, which renamed FastMCP
and broke CI), which resolves mcp to 1.29.1. Verified in a throwaway venv built
from scratch with the current pins: pip-audit with zero ignore flags reports
No known vulnerabilities found, exit 0. Corroborated by CI, where the audit step
passed on Python 3.11 and 3.12 while the flag it still carried matched nothing..github/workflows/ci.yml and the Gate 4 checklist in
CLAUDE.md. This was the project’s last --ignore-vuln flag.<2 ceiling holding mcp on a patched 1.x.
The eventual mcp 2.x migration (#310) must re-run pip-audit and confirm 2.x is
also unaffected before that ceiling is lifted.starlette (transitive via the mcp SDK’s HTTP/SSE transport). This server
runs stdio transport only (see server.py:main()), so the affected request-handling
code paths were never reached at runtime — the exposure was theoretical.mcp pin to raise the starlette floor.pyproject.toml now pins starlette>=1.3.1 directly
rather than waiting on mcp. mcp requires only starlette>=0.27, so the stricter floor
is compatible. pip-audit reports no starlette advisories at 1.3.1. Nothing is added
to --ignore-vuln for starlette.The following 14 CVEs were identified in a pip-audit run on 2026-05-22 as part of
the Phase 17 Gate 2 review. None are exploitable via this server’s code paths.
They are documented here for tracking but are NOT added to the --ignore-vuln
list — each requires an explicit decision before ignoring. Re-evaluate when a fix
becomes available in the dependency tree.
cryptography (transitive via mcp SDK)mcp when upstream updates to cryptography ≥ 46.0.7.idna (transitive via requests)www.acinfinityserver.com (a simple ASCII hostname); the IDNA
vulnerability (malformed label handling) is not reachable.requests or idna directly once 3.15 is
available in the dependency tree.pip (dev tool — not a runtime dependency)pip is not imported or used by the server at runtime.
Upgrade pip in the build/dev environment: python3 -m pip install --upgrade pip.pygments (dev tool — pulled in by rich/IPython for terminal output)pygments is not imported or used at runtime.python-multipart (transitive via mcp → starlette)mcp SDK’s HTTP/SSE transport. This server runs
stdio transport only — the multipart parsing code paths are never invoked.mcp bumps its starlette/
python-multipart pins.setuptools (build/install tool — not a runtime dependency)setuptools is used during package installation;
it is not imported at runtime.pip install --upgrade setuptools.pip-audit and capture the CVE ID + package.--ignore-vuln <ID> to the pip-audit invocation in
.github/workflows/ci.yml with an inline comment pointing here.