ApexMCP
Talks directly to a Neptune Apex aquarium controller's local REST API (e.g. Apex, AC6L, AOS 5.15L firmware) over stdio, without MQTT, Home Assistant or Neptune Fusion cloud. Read-only by default, it exposes tools to query system identity/firmware/modules/feed state, current probe readings, water parameters (temp, pH, ORP, salinity, Trident chemistry), power summary (watts/amps, voltage, top consumers), digital float switch states, outlet states (AUTO/ON/OFF, energized, pump mode), config sections, probe history and drift statistics, outlet on/off event logs, duty-cycle analytics, and outlet program text. When writes are explicitly enabled it can set outlets to AUTO/ON/OFF, start and safely cancel feed cycles, and diff/backup/apply/re-verify outlet program edits including MXM tdata time tables, with protected outlets, credential redaction and rate-limit-aware caching.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@ApexMCPwhat's my tank temperature and pH right now?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
ApexMCP
A standalone MCP server for Neptune Apex aquarium controllers. It talks straight to the controller's local REST API. No MQTT broker, no Home Assistant, no Neptune Fusion cloud login.
Read-only by default. Outlet control, feed cycles and program edits each have to be switched on.
Not affiliated with Neptune Systems. The Apex REST API is undocumented and can change with firmware. Tested only against AOS 5.15L on an AC6L.
Install
Python 3.11+.
pip install -e .Related MCP server: Tesla MCP Server
Configure
Everything is an environment variable.
Variable | Default | Notes |
| required |
|
|
| |
| empty | Or |
|
| Enables |
|
| Also enables |
| none | Comma-separated device IDs or names ( |
|
| Where old programs are saved before an edit. |
|
|
Disabled tools are not registered at all, so the model never sees them.
Claude Desktop (Chat tab) and Claude Code
Claude Desktop reads %APPDATA%\Claude\claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/claude_desktop_config.json). Add the block below under mcpServers and fully restart the app. Claude Code takes the same definition: save it as JSON and run claude mcp add-json apex "<json>" --scope user (on Windows PowerShell this is more reliable than claude mcp add ... -- python -m apexmcp, which mangles -m).
The server runs on your own machine over stdio, so it works in the desktop app but not in claude.ai in a browser or the mobile app. The settings are read once at startup, so restart the client after changing the password file or any variable.
{
"mcpServers": {
"apex": {
"command": "python",
"args": ["-m", "apexmcp"],
"env": {
"APEX_HOST": "http://apex.local",
"APEX_PASSWORD_FILE": "C:\\path\\to\\apex-password.txt",
"APEX_ALLOW_WRITE": "true",
"APEX_PROTECTED_OUTLETS": "ReturnPump,Heater200"
}
}
}
}Use the Python from the environment where you ran pip install. Leave out the two ALLOW lines for a read-only server.
Features
Area | ApexMCP |
Host config | Works with a hostname ( |
Security | Credentials are stripped from every response, and the config's |
Writes | Off by default. Write tools are not registered unless enabled, and program edits need a separate flag |
Program edits | Diff first, confirm to apply, backup file, re-read to verify, directive check |
Protected outlets |
|
Feed cancel | Refuses when no feed is running, because that corrupts the feed block for about 17 hours |
Controller politeness | 3 s status cache. 429 is surfaced with |
Outlet state parsing | Parsed into mode, energized, percent and pump mode |
History and analytics | Probe history, drift stats, outlet on/off events, duty cycle |
Water parameters |
|
Power |
|
Digital inputs |
|
Fusion cloud | None, by design |
Home Assistant | None, by design |
Dosing | Coming soon |
Auto testing | Coming soon |
Tools
Always available:
Tool | What it does |
| Identity, firmware, modules, feed state, power-failure times, network quality |
| Current readings, optionally by type |
| Every outlet: mode (AUTO/ON/OFF), energized, power level, pump mode |
| Temp, pH, ORP, salinity and Trident chemistry (alk, Ca, Mg, NO3, PO4) when fitted; lists what is not available |
| Total watts and amps, line voltage, top consumers. Needs an energy bar such as the EB832 |
| Float switches and other digital inputs as OPEN or CLOSED |
| Running feed cycle and seconds remaining |
| Aquabus module presence and packet error rate |
| One config section, credentials redacted |
| Controller log for probes (default Temp and pH), downsampled |
| min/mean/max, daily means, day-over-day drift |
| Exact on/off times from |
| Percent on, run count, mean run length |
| Program text for one or all outlets |
With APEX_ALLOW_WRITE:
Tool | What it does |
| AUTO / ON / OFF. Re-reads the controller and reports before and after. |
| Feed A to D |
| Cancels only when a real feed is running |
With APEX_ALLOW_PROGRAM_WRITE:
Tool | What it does |
| Returns a diff by default. With |
Editing MXM tables (tdata)
MXM lights and pumps run on time tables. On the AOS 5.16L controller this was developed against, the table is part of the outlet's program text, one row per line:
Fallback OFF
tdata 22:50:00,0,0,10,100,0,0,0,0,0,0,0,0,0
tdata 23:59:00,0,0,8,100,0,0,0,0,0,0,0,0,0How to read a row:
HH:MM:SSis the time the row takes effect. The two zeros that follow are unused.Lights: the next value is the intensity percent. After it come the per-colour channel values, in the order of the outlet's
colorslist. Read that list withapex_get_config(sectionoconf, entryextra.colors). Unused channels are0.Pumps: after the intensity comes a mode ID. The IDs and names are in the outlet's
extra.modes.Per earlier reverse-engineering, the controller ramps between rows and wraps at midnight. This has not been re-verified on 5.16L.
A trailing line such as
Set OFFoverrides the table. Read the whole program before concluding that a table drives an outlet.
To change a row:
Enable
APEX_ALLOW_WRITEandAPEX_ALLOW_PROGRAM_WRITE, and make sure the outlet is not inAPEX_PROTECTED_OUTLETS.Read the program with
apex_get_program.Send the full program back through
apex_update_programwith only the row you want changed. Copy an existing row and edit its values rather than writing one from scratch, because channel counts and order depend on the fixture.Check the diff. It should show exactly the lines you meant to change. Then call it again with
confirm=true.The old program is saved as JSON in
APEX_BACKUP_DIRfirst. To undo, send that program's text back throughapex_update_program. There is no separate restore tool.
Status: one edit has been verified, changing a single row on a Radion lunar channel (AOS 5.16L). The diff touched only that line and a re-read matched. Not yet tested: pump tables, adding or removing rows, and Prime lights.
Things to watch:
The tool checks that each line starts with a known directive. It does not check the numbers inside a
tdatarow, so a malformed row can break a schedule. Use the diff.Anything else that writes tables, such as a lunar-phase scheduler, will overwrite your edit on its next run.
The change takes effect on the controller immediately.
Safety notes
See SECURITY.md for reporting a problem and the known limits, including prompt injection through controller-supplied names and programs.
/rest/configcontains the controller admin password in plaintext. It is stripped from every response, along with the Fusion link key.nconfis not available as a config section.Manual
ON/OFFoverrides an outlet's program until you set it back toAUTO. Heaters and return pumps are the obvious candidates forAPEX_PROTECTED_OUTLETS.Cancelling a feed while none is running corrupts the controller's feed block for about 17 hours (
name: 256, underflowed countdown).apex_cancel_feedrefuses to do it, and feed data is range-checked on read.The controller has no HTTPS on the LAN, so the admin password travels in clear text. Change the default password.
Status is cached for 3 seconds and 429 responses surface as an error, because the Apex rate-limits.
Firmware notes
Learned the hard way, and encoded in apexmcp/model.py:
Outlet state is
status[0](AON,AOF,TBL,ON,OFF). The first three mean the outlet is under program control./rest/iloglogs inputs only./rest/ologlogs outlet changes. For both,sdate(YYMMDD) is the start date and the log runs from there tosdate + days, clipped at now. Treatingilog'ssdateas an end date, as some older write-ups do, returns only today.Temperature comes with no unit. Values of 45 or below are guessed as Celsius.
Day-over-day drift is used instead of a straight-line fit, which reports phantom drift over a window that is not a whole number of daily cycles.
The /rest/olog response shape is parsed defensively and has not yet been checked against a live controller. Run apex_outlet_events against a logged outlet first.
Develop
pip install -e ".[dev]"
pytestThe tool logic in apexmcp/tools.py is independent of the MCP framework and is tested with a fake controller.
Credits and license
The Apex client and status parser come from ReefHub. AGPL-3.0, see LICENSE.
This server cannot be deployed
Maintenance
Related MCP Connectors
Read NodePing checks, uptime, results and events; mute checks, manage maintenance and schedules.
Read Adafruit IO feeds, data, groups and dashboards; publish a data point or create a feed.
Manage Accelo companies, contacts, jobs, tasks, tickets and time.
- AskMyEVOAuthcom.askmyev
Tesla data for your AI: battery, range, charging, tires, alerts, history. Controls off by default.
Related MCP Servers
- AlicenseAqualityDmaintenanceEnables monitoring and controlling KegLand RAPT fermentation chambers and RaptPill hydrometers through the RAPT.io API, including temperature control, PID configuration, and telemetry retrieval.23MIT
- AlicenseNot gradedqualityDmaintenanceEnables control and monitoring of Tesla vehicles via the Tessie API, providing state and command tools for climate, locks, charging, and navigation.MIT
- AlicenseNot gradedqualityDmaintenanceEnables managing VyOS appliances through their HTTP API, providing tools for configuration, operational commands, and interactive troubleshooting.MIT
- AlicenseBqualityAmaintenanceEnables MCP-compatible clients to query and manage LenelS2 S2 NetBox physical access-control systems via the NBAPI, with read-only tools by default and opt-in write/destructive operations.5192 npmMIT