bitaxe-mcp
A single-process MCP server for monitoring and configuring
BitAXE Bitcoin ASIC miners. Shipped as the installable
bitaxe-mcp distribution (import bitaxe_mcp) — exposes tool functions that proxy HTTP
requests to miner APIs, no boilerplate, no framework weight.
Features
- Monitor miner stats, ASIC capabilities, system info, and WiFi networks in real time
- Configure pools, fans, frequency/voltage, display settings, and more via generic patch tools
- Maintain miners remotely with restart and identify functions
- Docker-ready — multi-stage build + host-network deployment for LAN-located miners
Architecture
bitaxe-mcp/
├── src/
│ └── bitaxe_mcp/ # importable package (shipped in the wheel)
│ ├── __init__.py # docstring + __version__ (from installed metadata)
│ ├── config.py # MINERS_CONFIG env parser (pydantic), IP resolution, cache
│ ├── client.py # _http_request() helper, _format_json, timeout constants
│ ├── discovery.py # subnet detection + concurrent LAN host probing
│ ├── server.py # FastMCP instance, lifespan, main() entry point
│ └── tools/ # @mcp.tool() functions grouped by domain
│ ├── monitor.py # status, stats, asic_info, scan_wifi (GET)
│ ├── configure.py # configure, set_fan, set_frequency (PATCH)
│ ├── pool.py # set_pool (PATCH)
│ ├── maintenance.py # restart, identify, list_miners (POST/config)
│ └── discover.py # discover (LAN scan)
├── .env.example # Example environment configuration
├── pyproject.toml # uv project manifest — deps, dev-deps, pytest config, build backend
├── tests/
│ ├── conftest.py # autouse fixtures: mock transport, seeded miners
│ ├── test_config.py # config parsing and validation (8 cases)
│ └── test_server.py # all 12 tool functions + error paths (40+ cases)
├── Dockerfile # multi-stage build (builder → slim runtime)
├── docker-compose.yml # host-network deployment, env file bind
└── AGENTS.md # Open-code agent instructions (architecture, gotchas, endpoints table)
Key design: tools call bitaxe_mcp.client._http_request() to construct URLs and manage
AsyncClient lifecycle. There is no router or middleware — each tool is a standalone async
function wired via @mcp.tool() on the single shared FastMCP instance in bitaxe_mcp.server.
Installation
Requires: Python ≥3.12, uv (recommended) or pip.
# From a source checkout
git clone <repo-url> && cd bitaxe-mcp
# Install into a venv (dependencies + the package itself); creates .venv with uv
uv sync # uv
uv pip install . # uv without the project layout
pip install . # pip
# Or from a pre-built wheel
pip install dist/bitaxe_mcp-0.1.0-py3-none-any.whl
# Build a wheel + sdist from source
uv build
Installing the package provides the bitaxe_mcp import plus the bitaxe-mcp console command,
and the import bitaxe_mcp API surface (bitaxe_mcp.config, bitaxe_mcp.client,
bitaxe_mcp.discovery, bitaxe_mcp.tools, bitaxe_mcp.server).
Configuration
Create .env from the example and add your miners:
cp .env.example .env
# edit .env → update MINERS_CONFIG
The MINERS_CONFIG environment variable is a JSON dict mapping miner names to their LAN IP addresses.
Both formats are accepted:
# Flat format (auto-wrapped into miners key)
MINERS_CONFIG='{"alpha-1":{"ip":"10.0.0.1"},"beta-1":{"ip":"10.0.0.2"}}'
# Explicit miners key
MINERS_CONFIG='{"miners":{"alpha-1":{"ip":"10.0.0.1"}}}'
Running
Local
The server runs in stdio transport mode — clients connect via the MCP SDK:
bitaxe-mcp # console command from an installed package
python -m bitaxe_mcp.server # equivalent, from a source checkout or venv
On startup, server_lifespan() loads and validates MINERS_CONFIG. If config is missing or empty
a ValueError is raised at first tool call (not at import). The server itself starts cleanly either way.
Via uvx / uv tool
uvx can install the package into an ephemeral environment and
run the bitaxe-mcp console command in one step — no persistent installation required:
# From a local directory or git URL (works today, no publishing required)
MINERS_CONFIG='{"alpha-1":{"ip":"10.0.0.1"}}' uvx --from . bitaxe-mcp
MINERS_CONFIG='{"alpha-1":{"ip":"10.0.0.1"}}' uvx --from git+https://github.com/nuxnik/bitaxe-mcp bitaxe-mcp
# From a published package (once available on your package index)
MINERS_CONFIG='{"alpha-1":{"ip":"10.0.0.1"}}' uvx bitaxe-mcp
uvxdoes not load.envfiles — provideMINERS_CONFIGdirectly on the command line (as above), viadirenv/shell exports, or by passing it before the command.
Docker
docker compose up --build
The container uses network_mode: host to reach miners on the local LAN.
MCP Tools
| Tool | Method | Purpose |
|---|---|---|
bitaxe_status(miner) |
GET /api/system/info |
Full system info (model, hash rate, temp, fan speed) |
bitaxe_stats(miner,\n columns?) |
GET /api/system/statistics?columns=... |
Statistics — hashrate, power, temperature. Pass column names like ["hashrate","power"] |
bitaxe_asic_info(miner) |
GET /api/system/asic |
ASIC capabilities: model, default frequency, voltage/frequency options |
bitaxe_scan_wifi(miner) |
GET /api/system/wifi/scan |
Available WiFi networks near the miner |
bitaxe_configure(miner,\n settings) |
PATCH /api/system |
Generic — apply arbitrary key-value pairs to any setting group below |
bitaxe_set_fan(miner,\n fanspeed?,\n autofanspeed?) |
PATCH /api/system |
Set fan speed (0–100%) or toggle auto-fan mode |
bitaxe_set_frequency(miner,\n frequency,\n core_voltage) |
PATCH /api/system |
Overclock ASIC: set frequency + core voltage (auto-enables overclock) |
bitaxe_set_pool(miner,\n url,\n port?,\n user?,\n fallback_url?,\n ...) |
PATCH /api/system |
Configure primary and/or fallback BTC stratum pool |
bitaxe_restart(miner) |
POST /api/system/restart |
Remote reboot the miner |
bitaxe_identify(miner) |
POST /api/system/identify |
Blink front-panel LED / beep to locate a physical miner |
bitaxe_discover(subnet?) |
GET /api/system/info (per host) |
Scan the LAN for active BitAXE devices; optional CIDR (e.g. 192.168.1.0/24), defaults to the host's own subnet. Returns { "subnet", "count", "devices": [{ "ip", "asicModel", "hostname" }] } |
Settings groups (for bitaxe_configure)
- Pool:
stratumURL,stratumPort,stratumUser,stratumPassword,fallbackStratumURL, ... - WiFi:
ssid,wifiPass,hostname - ASIC:
coreVoltage,frequency,overclockEnabled - Fan:
autofanspeed,fanspeed,temptarget - Display:
rotation,invertscreen,displayTimeout - Advanced:
overheat_mode,statsFrequency
Every tool returns a pretty-printed JSON string.
bitaxe_configurewraps the response in{ "applied": settings, "response": <miner-api.json> }for auditability.
Running Tests
Use the dedicated run_tests.py script to execute the test suite:
# Run all tests
./run_tests.py
# Verbose output
./run_tests.py -v
# Specific file
./run_tests.py tests/test_config.py
# Collect without running
./run_tests.py --collect-only
# With coverage
./run_tests.py --cov=bitaxe_mcp --cov-report=term-missing
The script passes all flags directly to pytest. No separate uv run command needed.
Tests
Tests use conftest.py autouse fixtures: a seeded MINERS_CONFIG with one "test-miner" entry,
and a mocked HTTP transport (mock_transport) that returns canned JSON for all miner API paths.
Unknown miner names raise ValueError (from config.get_miner_ip()).
Test coverage
| Module | Cases | What is tested |
|---|---|---|
test_config.py |
9 | Flat/explicit config format, invalid JSON, empty miners, IP resolution, pydantic validation, conftest injection |
test_server.py |
40+ | All 12 tools + parameter variants (fan ranges, pool required/optional fields, frequency body shape) + HTTP error status codes (500, 404) + unknown miner path + discovery scans |
Configuration Reference
| Env Var | Type | Description | Example |
|---|---|---|---|
MINERS_CONFIG |
JSON string | Maps miner names → { "ip": "..." } |
'{"miners":{"alpha-1":{"ip":"10.0.0.1"}}}' |
Project Structure Reference
bitaxe-mcp/
├── src/bitaxe_mcp/ # The importable package — see Architecture tree above
├── .env.example # Template for local development
├── pyproject.toml # uv project config: deps, dev-deps, pytest settings
│ — mcp[cli] + httpx + pydantic-settings + python-dotenv
│ — dev: pytest + pytest-asyncio
│ — hatchling build backend + bitaxe-mcp entry point
├── Dockerfile # Multi-stage: builder installs via uv → slim runtime
├── docker-compose.yml # Single service: build ., host network, env_file .env
└── AGENTS.md # Architecture & endpoints table for code agents
Configuration Tool Reference
| Setting | Type | Description | Example |
|---|---|---|---|
stratumURL |
string | Primary pool URL | "stratum+tcp://pool.btc.com" |
stratumPort |
int | Primary pool port | 3333 |
stratumUser |
string | Pool worker name | "worker1" |
stratumPassword |
string | Pool password | "x" |
fallbackStratumURL |
string | Fallback pool URL | "stratum+tcp://backup.com" |
ssid |
string | WiFi SSID (1-32 chars) | "MyNetwork" |
wifiPass |
string | WiFi password (8-63 chars) | "SecurePass123" |
coreVoltage |
int | ASIC core voltage (mV) | 850 |
frequency |
int | ASIC frequency (MHz) | 600 |
overclockEnabled |
0 or 1 | Enable/disable overclocking | 1 |
autofanspeed |
0 or 1 | Automatic fan control | 1 |
fanspeed |
int | Manual fan speed % (0-100) | 75 |
rotation |
int | Display rotation (degree) | 90 |
displayTimeout |
int | Screen off after N min (-1=always on) | -1 |
Docker Reference
| Directive | Value | Description |
|---|---|---|
build |
. |
Multi-stage build from project root |
network_mode |
host |
Required for LAN miner reachability |
env_file |
.env |
Binds local .env into container |
restart |
unless-stopped |
Auto-restart on crashes or host reboots |
Troubleshooting Reference
| Problem | Cause | Resolution |
|---|---|---|
| Miner unreachable | LAN firewall / wrong IP | Verify miner IP and port from your router |
| Config validation error | MINERS_CONFIG malformed | Run python -c "import json; print(json.loads(os.environ['MINERS_CONFIG']))" |
| Overclock fails | Miners must support frequency/voltage override hardware limits apply — check ASIC capabilities via bitaxe_asic_info) |
Check your miner's supported voltage/frequency ranges |
| Docker not reaching miners | Missing host network mode | Ensure network_mode: host in docker-compose.yml |
Git Reference
Standard git workflow. Commits are squashed; no history rewriting after push.
Contributors Guide
To add a new tool, mirror the existing patterns in src/bitaxe_mcp/tools/ closely — use
client._http_request(...) via the bitaxe_mcp.client module attribute, decorate with
mcp.tool() from bitaxe_mcp.server, and import the module from
src/bitaxe_mcp/tools/__init__.py so it registers on the shared FastMCP instance.
Release files for bitaxe-mcp 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| bitaxe_mcp-0.1.0.tar.gz | 56.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| bitaxe_mcp-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 72.6 kB
Release files / bitaxe_mcp-0.1.0.tar.gz
| Download URL | bitaxe_mcp-0.1.0.tar.gz |
|---|---|
| Size | 56.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
488fb3b1f007ef08cdbb057fe38ac002fdef20550c065ce007813bf8d2c533aa
|
|
BLAKE2b-256 checksum How to use checksums |
ea04788e9516b3f1da72777359191f281a9b42cc5fed934ef015e312ae8b1a90
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.7.11
|
Release files / bitaxe_mcp-0.1.0-py3-none-any.whl
| Download URL | bitaxe_mcp-0.1.0-py3-none-any.whl |
|---|---|
| Size | 16.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a3c466e8df1cbe25ffaa587f3c115e22b184bb636011a38c57cba5ddbc92ac48
|
|
BLAKE2b-256 checksum How to use checksums |
575d9427752f8279475c1479f3d0145929e1e2edb923eafb2fa5be6203194996
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.7.11
|