OPenSprinkler MCP Server

用于通过Claude Desktop或任何兼容MCP的客户端管理OpenSprinkler控制器的MCP服务器。

文档

OpenSprinkler MCP Server

MCP server for managing OpenSprinkler controller via Claude Desktop or any MCP-compatible client.

Restriction: Only one controller can be managed at a time. This can be changed in the future if needed, but for now it is a limitation of the design.

OpenSprinkler MCP Server – quality and maintenance score on Glama

Available Tools

ToolDescription
get_controller_statusGet controller status (device time, enabled state, rain delay)
get_stationsGet all stations with their current status
run_stationStart a station for a specified duration
stop_stationStop a running station
stop_all_stationsStop all running stations immediately
set_rain_delaySet rain delay in hours (0 to clear)
enable_controllerEnable or disable the controller
reboot_controllerReboot the OpenSprinkler controller
view_logsGet watering history logs for a date range
get_programsGet all watering programs
get_programGet full details of a single program by ID
add_programAdd a new watering program
delete_programDelete a watering program by ID
get_optionsGet controller options and settings

Configuration

Environment Variables

VariableDescriptionDefault
OPEN_SPRINKLER_HOSTHostname or IP address of OpenSprinklerlocalhost
OPEN_SPRINKLER_PASSWORDMD5 hash of the controller password(empty)
OPEN_SPRINKLER_PORTPort number80
OPEN_SPRINKLER_TLSSet to true to use HTTPSfalse
OPEN_SPRINKLER_TIMEOUT_MSAbort a controller request after this many milliseconds10000
PORTHTTP port for SSE mode3000
HOSTInterface to bind in SSE mode. Use 127.0.0.1 to accept only local connections0.0.0.0
MCP_AUTH_TOKENBearer token required on all HTTP requests (optional)(unset — no auth)
MCP_ALLOWED_HOSTSComma-separated Host header allowlist. When set, other hosts get 403 — this is what blocks DNS-rebinding attacks(unset — not validated)
MCP_ALLOWED_ORIGINSComma-separated Origin allowlist for browser clients. Requests with no Origin are unaffected(unset — not validated)

Secure Connection (TLS/HTTPS)

If your OpenSprinkler is behind a reverse proxy with HTTPS (e.g. nginx, Traefik), set:

OPEN_SPRINKLER_HOST=opensprinkler.example.com
OPEN_SPRINKLER_PORT=443
OPEN_SPRINKLER_TLS=true

The server will then connect via https://opensprinkler.example.com:443.

Note: OpenSprinkler firmware does not natively support TLS — HTTPS requires a reverse proxy in front of the controller.


Transport Modes

The server supports two transport modes, selected by a command-line argument.

stdio (default)

Used when launched directly by an MCP client (e.g. Claude Desktop). No argument needed:

node dist/index.js

SSE / HTTP (--sse)

Starts an HTTP server that exposes the MCP endpoint over Streamable HTTP (SSE). Use this when you want to run the server as a persistent network service — for example in Docker or Kubernetes — and connect to it from multiple clients or over the network.

node dist/index.js --sse

The server listens on http://${HOST}:${PORT}/mcp (default 0.0.0.0:3000).

Connect your MCP client to: http://<host>:3000/mcp

Bearer Authentication (HTTP mode only)

Set MCP_AUTH_TOKEN to require a bearer token on every HTTP request. If the variable is not set the server accepts all connections (useful for private networks or local use).

MCP_AUTH_TOKEN=mysecrettoken TRANSPORT=http node dist/index.js

Clients must send the header:

Authorization: Bearer mysecrettoken

Requests without a valid token receive 401 Unauthorized. Tokens are compared in constant time. The stdio transport is unaffected — authentication is not applicable there.

Network Hardening (HTTP mode only)

These tools can start and stop watering, disable the controller, reboot it and delete programs, so anyone who can reach the HTTP port can do all of that. In HTTP mode the server binds 0.0.0.0 by default and prints a warning at startup when no token is set.

For anything beyond a trusted private network:

MCP_AUTH_TOKEN=mysecrettoken \
HOST=127.0.0.1 \
MCP_ALLOWED_HOSTS=localhost:3000,127.0.0.1:3000 \
node dist/index.js --sse
  • MCP_AUTH_TOKEN is the primary control — without it the endpoint is open.
  • MCP_ALLOWED_HOSTS defends against DNS rebinding: a malicious web page can make a browser on your network reach 127.0.0.1:3000, but the request still carries the attacker's hostname in the Host header, which the allowlist rejects.
  • HOST=127.0.0.1 keeps the listener off the network entirely. Leave it at the 0.0.0.0 default inside Docker/Kubernetes, where the container needs to accept connections from outside its network namespace.

Legacy SSE (backport compatibility)

For testing with tools that implement the older MCP SSE transport (e.g. mcptools, older inspector versions), the server automatically handles legacy clients on the same /mcp endpoint — no extra flag needed.

How the server distinguishes clients:

RequestInterpreted as
GET /mcp (no mcp-session-id header)Legacy SSE — opens an event-stream
POST /mcp?sessionId=<id>Legacy SSE — client sending a message
POST /mcp (with or without mcp-session-id header)Modern Streamable HTTP

Connecting a legacy client (example with mcptools):

# Start the server
node dist/index.js --sse

# In another terminal
mcp connect http://localhost:3000/mcp

Note: SSEServerTransport is deprecated in the MCP SDK and this compatibility layer is intended for testing only. Production clients should use the modern Streamable HTTP transport.

You can enable verbose debug output during testing by setting NODE_ENV=development:

NODE_ENV=development node dist/index.js --sse

This prints every request, response status, and session lifecycle event to stderr.


Docker

See docs/docker/ for the full deployment guide, Docker Compose file, Claude Desktop configuration options, and troubleshooting steps.


Kubernetes

See docs/kubernetes/ for manifests. Adjust the ConfigMap and Secret for your environment, then:

kubectl apply -k docs/kubernetes/

Claude Desktop (Node.js, no Docker)

{
  "mcpServers": {
    "opensprinkler": {
      "command": "node",
      "args": ["/path/to/opensprinkler-mcp/dist/index.js"],
      "env": {
        "OPEN_SPRINKLER_HOST": "192.168.1.100",
        "OPEN_SPRINKLER_PASSWORD": "your_password"
      }
    }
  }
}

Password

The OpenSprinkler firmware stores whatever string was written when the password was last set. The official app MD5-hashes the password before sending it, so the stored value is an MD5 hash — and OPEN_SPRINKLER_PASSWORD must be the MD5 hash of your password, not the plain text.

Generate it with:

echo -n "your_password" | md5

Contributing

Contributions are welcome. Please open an issue or pull request on GitHub.

Acknowledgements

This project is inspired by the work at github.com/Lumeo-sd/opensprinkler-mcp-sdr. All code was automatically re-created from scratch using AI tools due to lack of LICENSE statement in the original repository.

Disclaimer

  • I am not affiliated with the manufacturers, sellers or any of the device brands. This plugin is a personal project that I maintain in my free time.
  • Refer to the licence for more information.