just-a-mysql-mcp
# just-a-mysql-mcp
Browse MySQL schema and run **read-only** SQL from your AI agent.
> **Read-only by design.** Tools never INSERT / UPDATE / DELETE / DDL. An agent writing to the wrong table or environment can cause production-level incidents. Just because we can doesn't mean we should. Prefer a MySQL account that is itself read-only.
## What it does
- **Database scope** — Allowlist + Default Database for this process
- **List tables** — names in one Allowlisted Database
- **Describe a table** — columns and types (explicit Database + table)
- **Run a query** — one read-only statement (`SELECT` / `WITH…SELECT`, `SHOW`, `DESCRIBE`/`DESC`, `EXPLAIN`), with a hard row cap; visited Databases must be on the Allowlist
One MCP process is bound to an **Allowlist** of Databases (`MYSQL_DATABASE`: one name or a comma-separated list). Size one is strict single-Database; size many permits exactly those names.
## Requirements
- [uv](https://docs.astral.sh/uv/) — `brew install uv`
- MySQL 5.7+ / 8.x reachable over TCP
## Setup
**1. Clone and install**
```bash
git clone https://github.com/xingyuli/just-a-mysql-mcp
cd just-a-mysql-mcp
uv sync
```
**2. Add to your AI agent**
Single Database:
```json
{
"mcpServers": {
"mysql": {
"command": "uv",
"args": [
"--directory", "/path/to/just-a-mysql-mcp",
"run", "mysql_mcp_server.py"
],
"env": {
"MYSQL_HOST": "127.0.0.1",
"MYSQL_PORT": "3306",
"MYSQL_USER": "readonly_user",
"MYSQL_PASSWORD": "secret",
"MYSQL_DATABASE": "your_database"
}
}
}
}
```
Multiple Databases (stable Allowlist order; optional default override):
```json
"env": {
"MYSQL_HOST": "127.0.0.1",
"MYSQL_PORT": "3306",
"MYSQL_USER": "readonly_user",
"MYSQL_PASSWORD": "secret",
"MYSQL_DATABASE": "app_dev,billing_dev",
"MYSQL_DEFAULT_DATABASE": "billing_dev"
}
```
Shell exports work too:
```json
"MYSQL_HOST": "${MYSQL_DEV_HOST}",
"MYSQL_USER": "${MYSQL_DEV_USER}",
"MYSQL_PASSWORD": "${MYSQL_DEV_PASSWORD}",
"MYSQL_DATABASE": "app_dev"
```
**3. Restart the agent** and ask things like:
- *"What databases are in scope?"*
- *"List tables in app_dev"*
- *"Describe users in app_dev"*
- *"Select the 10 most recent rows from …"*
## Configuration
| Variable | Required | Default | Notes |
|---|---|---|---|
| `MYSQL_HOST` | No | `127.0.0.1` | |
| `MYSQL_PORT` | No | `3306` | |
| `MYSQL_USER` | **Yes** | | |
| `MYSQL_PASSWORD` | **Yes** | | No short alias |
| `MYSQL_DATABASE` | **Yes** | | One name or comma-separated Allowlist |
| `MYSQL_DEFAULT_DATABASE` | No | First name in `MYSQL_DATABASE` | Must be on the Allowlist |
## Safety summary
| Rule | Behavior |
|---|---|
| Writes | Rejected by the MCP; use a read-only DB user too |
| Statements | Exactly one per `mysql_query` call |
| Statement types | `SELECT`, `WITH`, `SHOW`, `DESCRIBE`/`DESC`, `EXPLAIN` |
| Database scope | sqlglot visit check ⊆ Allowlist; unqualified → Default Database; `USE` rejected; parse errors fail closed |
| Row cap | Default 100, max 1000; truncation is explicit |
Design notes: [CONTEXT.md](CONTEXT.md), [docs/adr/0001-v1-design.md](docs/adr/0001-v1-design.md), [docs/adr/0002-database-allowlist.md](docs/adr/0002-database-allowlist.md).
---
For local development and tests, see [DEVELOPMENT.md](DEVELOPMENT.md).
TDQS
Scored across 3 tools
The tools are mostly distinct in purpose: list_tables for table names, describe_table for column details, and mysql_query for arbitrary read-only SQL. However, mysql_query also supports DESCRIBE/DESC, creating a partial overlap with describe_table.
list_tables and describe_table follow a clear verb_noun pattern, but mysql_query deviates with a noun_verb structure. All names are snake_case and readable, so the inconsistency is minor.
Three tools is a minimal but well-scoped set for a read-only MySQL server. It is slightly on the small side, but each tool serves a distinct core need and the count fits the intentional minimalism.
The set covers the essential read-only workflow: discovering tables, inspecting schema, and running queries. Missing explicit tools for views/indexes are easily handled via mysql_query, so gaps are minor.