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.
This commit is contained in:
70
README.md
Normal file
70
README.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# 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
|
||||
"
|
||||
```
|
||||
Reference in New Issue
Block a user