solax-cloud-mcp
by mouldiwarp
README.md
# SolaX Cloud MCP Server
An MCP server that provides real-time access to solar inverter data from the SolaX Developer Platform API. Query your inverter's current power output, energy yields, battery status, and grid import/export data directly from Claude Code or Claude Desktop.
## Prerequisites
- **Python 3.10+** (automatically provisioned by `uv`)
- **uv** package manager ([install here](https://astral.sh/uv/))
- A SolaX Developer Platform account with OAuth2 application registered
- Device (inverter) serial number (`deviceSn`) for your inverter
## Getting Started
### 1. Register an OAuth2 Application
1. Log in to [SolaX Developer Platform](https://developer.solaxcloud.com/)
2. Navigate to **Application** section
3. Create a new application and enable **client_credentials** grant type
4. Copy your **Client ID** and **Client Secret** (keep these secret!)
### 2. Identify Your Device Serial Number
1. Log in to [SolaX Developer Platform](https://developer.solaxcloud.com/)
2. Navigate to **My Account** or device management section
3. Find your inverter's **device serial number** (e.g., `X3ABCD0123`)
- This is NOT the old WiFi dongle registration number used by the legacy SolaX Cloud API
### 3. Install the Server
```bash
# Clone or navigate to the repo
cd /Users/gary/mysrc/claude/solax-cloud-mcp
# Install uv if needed
curl -LsSf https://astral.sh/uv/install.sh | sh
# Install dependencies and create virtual environment
uv sync
```
### 4. Configure Credentials
```bash
# Copy the example environment file
cp .env.example .env
# Edit .env and fill in your credentials
nano .env
```
Add your OAuth2 credentials and device serial number:
```
SOLAX_CLIENT_ID=your_client_id
SOLAX_CLIENT_SECRET=your_client_secret
SOLAX_DEVICE_SN=X3ABCD0123
```
### 5. Register with Claude Code
To use this server with Claude Code or Claude Desktop:
```bash
claude mcp add solax-cloud \
--env SOLAX_CLIENT_ID=your_client_id \
--env SOLAX_CLIENT_SECRET=your_client_secret \
--env SOLAX_DEVICE_SN=your_device_sn \
--scope user \
-- uv run --directory /Users/gary/mysrc/claude/solax-cloud-mcp solax-cloud-mcp
```
If the above doesn't work (environment variable propagation issues), edit your MCP config JSON directly:
- **Claude Code:** `~/.claude/mcp.json` or project settings
- **Claude Desktop:** `~/.config/Claude/claude_desktop_config.json` (macOS) or equivalent
Add this entry:
```json
{
"solax-cloud": {
"command": "uv",
"args": [
"run",
"--directory",
"/Users/gary/mysrc/claude/solax-cloud-mcp",
"solax-cloud-mcp"
],
"env": {
"SOLAX_CLIENT_ID": "your_client_id",
"SOLAX_CLIENT_SECRET": "your_client_secret",
"SOLAX_DEVICE_SN": "your_device_sn"
}
}
}
```
## Usage
Once registered, the `get_realtime_data` tool is available in your MCP toolset. Use it to query real-time inverter data.
### Tool: `get_realtime_data`
**Arguments:**
- `device_sn` (optional): Inverter device serial number. If omitted, defaults to `SOLAX_DEVICE_SN` environment variable.
**Returns:** A structured dictionary containing:
```json
{
"device": {
"deviceSn": "X3ABCD0123",
"registerNo": "SE123456SP",
"dataTime": "2025-05-22 15:13:10",
"plantLocalTime": "2025-05-22 15:13:10"
},
"status": {
"code": 102,
"description": "Normal"
},
"pv": [
{"string": 1, "voltage_V": 421.7, "current_A": 0.4, "power_W": 189.0},
{"string": 2, "voltage_V": 418.0, "current_A": 0.0, "power_W": 0.0}
],
"mppt": {
"trackers": [
{"mppt": 1, "voltage_V": 0.0, "current_A": 0.0, "power_W": 0.0}
],
"totalPower_W": null
},
"ac": {
"phases": [
{"phase": 1, "voltage_V": 224.1, "current_A": 0.9, "power_W": 189.0, "frequency_Hz": 50.0},
{"phase": 2, "voltage_V": 226.5, "current_A": 0.8, "power_W": 171.0, "frequency_Hz": 50.0}
],
"totalPower_W": 360.0,
"totalReactivePower": 0,
"powerFactor": 1.0,
"gridFrequency": 50.0
},
"energy": {
"dailyYield_kWh": 157.4,
"totalYield_kWh": 20465.3,
"dailyACOutput_kWh": 160.9,
"totalACOutput_kWh": 19907.5
},
"meter1": {
"gridPower_W": 0,
"todayImportEnergy_kWh": 0.07,
"totalImportEnergy_kWh": 23.28,
"todayExportEnergy_kWh": 0.0,
"totalExportEnergy_kWh": 64.98
},
"meter2": {
"gridPower_W": 0,
"todayImportEnergy_kWh": 0.08,
"totalImportEnergy_kWh": 1.39,
"todayExportEnergy_kWh": 0.0,
"totalExportEnergy_kWh": 0.75
},
"battery": {
"soc_percent": 85.5,
"remainingEnergy_kWh": 1024.8,
"soh_percent": 99.2,
"chargeDischargePower_W": -150.5,
"voltage_V": 409.6,
"current_A": -15.2,
"temperature_C": 28.3,
"cycleTimes": 142,
"totalCharge_kWh": 4250.75,
"totalDischarge_kWh": 4100.25,
"status": {"code": 1, "description": "Work"}
},
"eps": {
"voltage_V": [0.0, 0.0, 0.0],
"current_A": [0.0, 0.0, 0.0],
"activePower_W": [0.0, 0.0, 0.0],
"apparentPower_W": [0.0, 0.0, 0.0]
},
"temperature": {
"inverter_C": 40.4
},
"misc": {
"l1l2Voltage_V": null,
"l2l3Voltage_V": null,
"l1l3Voltage_V": null
}
}
```
### Example Usage in Claude
> "What's the current power output of my solar inverter?"
Claude will call `get_realtime_data()` and report the results to you in human-friendly terms.
## Docker: Build, Deploy & HTTP Consumption
For containerized deployment on Raspberry Pi or any Docker-enabled system, you can run the server in HTTP mode:
### Build the Docker Image
```bash
# Build the image
docker build -t solax-cloud-mcp:latest .
# Verify the build
docker images | grep solax-cloud-mcp
```
### Deploy with Docker Compose
Configure your environment variables first:
```bash
# Copy and edit the environment file
cp .env.example .env
nano .env
```
Fill in your SolaX credentials and generate a strong API key:
```env
SOLAX_CLIENT_ID=your_client_id
SOLAX_CLIENT_SECRET=your_client_secret
SOLAX_DEVICE_SN=your_device_sn
HTTP_API_KEY=$(python -c "import secrets; print(secrets.token_urlsafe(32))")
```
Start the HTTP server:
```bash
# Build and start the container in the background
docker-compose up -d
# Check logs
docker-compose logs -f solax-http
# Verify it's running
curl http://localhost:8000/health
```
The server listens on port **8000** and is accessible at `http://YOUR_PI_IP:8000`.
### Consume the HTTP API
All endpoints (except `/health`) require a bearer token in the `Authorization` header.
#### Health Check (no authentication)
```bash
curl http://192.168.1.100:8000/health
```
Response:
```json
{"status": "ok"}
```
#### Get Real-Time Inverter Data
```bash
curl -X POST \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"device_sn": "X3ABCD0123"}' \
http://192.168.1.100:8000/api/realtime-data
```
Returns current power output, battery SOC, grid export/import, and more.
#### Set Battery Self-Use Mode
Configure battery charging/discharging thresholds:
```bash
curl -X POST \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"device_sn": "X3ABCD0123",
"min_soc": 20,
"charge_upper_soc": 80,
"charge_from_grid_enable": 1
}' \
http://192.168.1.100:8000/api/battery/self-use-mode
```
### Python / JavaScript Examples
Quick examples in Python and JavaScript are available in **[HTTP_API.md](HTTP_API.md)**, including:
- Polling for real-time updates
- Time-based battery scheduling
- Error handling patterns
- Rate limiting considerations
### Deployment on Raspberry Pi
Full deployment instructions (Docker installation, network setup, security, monitoring) are in **[DEPLOYMENT.md](DEPLOYMENT.md)**.
### Docker Management
```bash
# View logs
docker-compose logs -f solax-http
# Restart the server
docker-compose restart solax-http
# Stop the server
docker-compose down
# Rebuild after code changes
docker-compose up -d --build
# Check resource usage
docker stats solax-http
```
## Manual Testing
### Quick Smoke Test
Before registering with Claude, you can test the client directly:
```bash
uv run --env-file .env python -c \
"import asyncio; from solax_cloud_mcp.client import fetch_realtime_data; \
from solax_cloud_mcp.config import get_default_device_sn; \
print(asyncio.run(fetch_realtime_data(get_default_device_sn())))"
```
### Run Tests
```bash
uv run pytest -v
```
Tests include:
- Response shaping and status decoding
- Environment variable validation
- Error handling and edge cases (battery-less devices, null status codes)
- Dynamic PV/MPPT parsing
- Case-insensitive field access
### Interactive Inspection
If you have the MCP CLI tools installed:
```bash
uv run --env-file .env mcp dev src/solax_cloud_mcp/server.py
```
## Rate Limiting
This server respects SolaX Developer Platform's documented rate limits:
- **100 calls per minute** per token
- **1,000,000 calls per day** per token
The client automatically enforces a 0.7-second minimum spacing between calls, which keeps typical usage well within both limits. No manual rate limiting is needed.
## Data Fields Reference
All values are in SI units:
- **Power:** Watts (W)
- **Energy:** Kilowatt-hours (kWh)
- **Voltage:** Volts (V)
- **Current:** Amps (A)
- **Temperature:** Celsius (°C)
- **Frequency:** Hertz (Hz)
### Inverter Status Codes
The inverter's `status.code` is an integer from the table below (Appendix 6 from the SolaX Developer Platform API docs). Not all possible codes are listed; consult the full Appendix 6 table on `developer.solaxcloud.com/doc` for the complete set.
| Code | Status | Description |
|------|--------|-------------|
| 100 | Waiting | Waiting |
| 101 | Self-check | Self-check |
| 102 | Normal | Normal |
| 103 | Fault | Fault |
| 104 | Permanent Fault Mode | Permanent Fault Mode |
| 105 | Update Mode | Update Mode |
| 106 | EPS Check Mode | EPS Check Mode |
| 107 | EPS Mode | EPS Mode |
| 108 | Self-test | Self-test |
| 109 | Idle Mode | Idle Mode |
| 110 | Standby Mode | Standby Mode |
| 130 | VPP mode | VPP mode |
| 131 | TOU-Self use | TOU-Self use |
| 132 | TOU-Charging | TOU-Charging |
| 133 | TOU-Discharging | TOU-Discharging |
| 1301-1309 | Advanced control modes | Power/SOC target control, self-consume modes, etc. |
### Battery Status Codes (Residential)
The battery's `battery.status.code` is an integer:
| Code | Status |
|------|--------|
| 0 | Idle |
| 1 | Work |
### API Error Codes (Appendix 1)
| Code | Message |
|------|---------|
| 10000 | Operation successful |
| 10001 | Operation failed |
| 11500 | System busy, please try again later |
| 10200 | Operation abnormality, please see the specific message content for details |
| 10400 | Request not authenticated |
| 10401 | Username or password incorrect |
| 10402 | Request access_token authentication failed |
| 10403 | Interface has no access rights |
| 10404 | Callback function not configured |
| 10405 | The number of API calls has been used up |
| 10406 | The API call rate has reached the upper limit, please try again later |
| 10500 | User has no device data permission |
| 10505 | Device unauthorized |
| 10506 | Plant unauthorized |
## Troubleshooting
**"SOLAX_CLIENT_ID environment variable not set"**
- Set the `SOLAX_CLIENT_ID` environment variable or register the server with the correct credentials
**"SOLAX_CLIENT_SECRET environment variable not set"**
- Set the `SOLAX_CLIENT_SECRET` environment variable or register the server with the correct credentials
**"No device_sn provided"**
- Either pass the `device_sn` argument to the tool or set the `SOLAX_DEVICE_SN` environment variable
**"SolaX API error 10402: Request access_token authentication failed"**
- The server will automatically attempt to refresh the access token once; if this persists, your Client Secret may be invalid or revoked. Re-register your OAuth2 application at https://developer.solaxcloud.com/
**"SolaX API error 10505: Device unauthorized"**
- The device serial number you provided is invalid or not associated with your account. Double-check the device SN in your SolaX Developer Platform account.
**"SolaX API error 10406: The API call rate has reached the upper limit"**
- The rate limiter is correctly enforced; this should rarely occur under normal usage. If it does, the server automatically backs off. Reduce tool call frequency or wait a few seconds and retry.
**"Network timeout"**
- SolaX Developer Platform API is unreachable. Check your internet connection and confirm the service is online at `https://developer.solaxcloud.com/`.
## Development
### Project Structure
```
solax-cloud-mcp/
├── src/solax_cloud_mcp/
│ ├── __init__.py # Package metadata
│ ├── __main__.py # CLI entry point
│ ├── server.py # MCP server and tool definitions
│ ├── client.py # HTTP client and API calls
│ ├── auth.py # OAuth2 token management
│ ├── config.py # Environment variable handling
│ └── models.py # Data models and response shaping
├── tests/
│ ├── fixtures/ # Test data
│ ├── test_config.py # Configuration tests
│ └── test_models.py # Response shaping tests
├── pyproject.toml # Project metadata and dependencies
└── README.md # This file
```
### Adding Features
The server is designed to be minimal and focused. To add more endpoints/tools:
1. Fetch the data from SolaX Developer Platform API (extend `client.py`)
2. Add a response shaping function if needed (extend `models.py`)
3. Define a new `@server.tool()` in `server.py`
## License
MIT
## Support
For issues with this MCP server, open an issue on the repository.
For SolaX Developer Platform API documentation, refer to [SolaX Developer Platform](https://developer.solaxcloud.com/doc).
TDQS
A4.3/5.0
Scored across 2 tools
Disambiguation5/5
The two tools have completely distinct purposes: one retrieves real-time data, the other sets a battery mode. There is no ambiguity between them.
Naming Consistency5/5
Both tools follow a consistent verb_noun snake_case pattern (get_realtime_data, set_battery_self_use_mode), making them predictable.
Tool Count2/5
With only 2 tools, the server feels too thin. A typical solar inverter API would require more tools (e.g., historical data, other modes, configuration) to be useful.
Completeness2/5
Major gaps exist: no tool for reading current battery mode, no historical data, no other inverter settings. The set covers only real-time data and one battery mode.
Maintenance
ActivitySlowing
ResponsivenessNo issues