Files
mcp-servers/README.md
help4bis 800610ae16 initial: forecastlab_db read-only MCP server
Read-only MariaDB facade for Claude Code. 4 tools:
list_databases / list_tables / describe_table / query.

Safety:
- 3 databases allowlisted (forecastlab, weewx_db, homeassistant)
- SELECT / SHOW / DESCRIBE / EXPLAIN / WITH only
- multi-statement injection rejected
- 1000 row hard cap (200 default)
- prefers MCP_DB_RO_USER if set; falls back to DB_USER

Tested: 10/10 read-only guard cases pass.
2026-04-26 15:47:18 +10:00

71 lines
2.2 KiB
Markdown

# mcp-servers
Locally-hosted MCP (Model Context Protocol) servers exposed to Claude Code.
Each subdirectory is one MCP server. Registered with Claude via:
```bash
claude mcp add-json -s user <name> '{"type":"stdio","command":"<path>","args":[...]}'
```
## Servers
| Name | Path | Purpose |
|---|---|---|
| `forecastlab_db` | `forecastlab_db/server.py` | Read-only SQL access to `forecastlab`, `weewx_db`, `homeassistant` databases |
## forecastlab_db
Read-only MariaDB facade. Tools: `list_databases`, `list_tables`, `describe_table`, `query`.
**Safety:**
- Only the 3 allowlisted databases reachable
- SELECT / SHOW / DESCRIBE / EXPLAIN / WITH only — write statements rejected
- Multi-statement injection rejected
- Hard cap 1000 rows per query (default 200)
- Datetimes serialised as ISO strings
**Optional read-only DB user** (recommended for production):
```sql
CREATE USER 'mcp_ro'@'localhost' IDENTIFIED BY '<secret>';
GRANT SELECT ON forecastlab.* TO 'mcp_ro'@'localhost';
GRANT SELECT ON weewx_db.* TO 'mcp_ro'@'localhost';
GRANT SELECT ON homeassistant.* TO 'mcp_ro'@'localhost';
FLUSH PRIVILEGES;
```
Then add to `~/.env`:
```
MCP_DB_RO_USER=mcp_ro
MCP_DB_RO_PASSWORD=<secret>
```
The server prefers these if set; otherwise falls back to `DB_USER`/`DB_PASSWORD`.
## Adding a new MCP server
1. Create a subdirectory: `mcp_servers/<name>/server.py`
2. Use the `mcp` Python SDK and the `FastMCP` helper (see `forecastlab_db/server.py` as a template)
3. Register with Claude:
```bash
claude mcp add-json -s user <name> \
'{"type":"stdio","command":"/home/help4bis/miniconda3/envs/forecastlab/bin/python","args":["/home/help4bis/lib/mcp_servers/<name>/server.py"]}'
```
4. Verify: `claude mcp list` should show it Connected
5. Commit + push to Gitea
## Testing
Each server's stdio-style protocol is best tested by Claude itself. For sanity-check the read-only guard:
```bash
/home/help4bis/miniconda3/envs/forecastlab/bin/python -c "
import sys; sys.path.insert(0, '/home/help4bis/lib/mcp_servers/forecastlab_db')
from server import _validate_read_only
_validate_read_only('SELECT 1') # passes
_validate_read_only('DROP TABLE foo') # raises ValueError
"
```