Skip to main content
Glama
xingyuli

just-a-mysql-mcp

by xingyuli
README.md
# 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

A4.1/5.0

Scored across 3 tools

Disambiguation4/5

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.

Naming Consistency4/5

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.

Tool Count4/5

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.

Completeness4/5

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.

Maintenance

ActivitySlowing
ResponsivenessNo issues