Local MySQL MCP Server
# Local MySQL Database MCP Server
A TypeScript Model Context Protocol (MCP) server that exposes **read-only**,
**least-privileged** access to a locally hosted MySQL database for approved
AI-agent workflows.
- [Documentation index](./docs/INDEX.md)
- [Tool reference](./docs/TOOLS.md)
- [Architecture](./docs/ARCHITECTURE.md)
- [Configuration](./docs/CONFIGURATION.md)
- [Security](./docs/SECURITY.md)
- [Database layer](./docs/DATABASE.md)
- [Logging and audit](./docs/LOGGING.md)
- [Development](./docs/DEVELOPMENT.md)
- [Threat model](./docs/THREAT_MODEL.md)
- [Project rules (`PUKU.md`)](./PUKU.md)
## Status
Implemented (Phases 0–8 of the plan). Unit, security, and integration tests
all run against your local MySQL — no container runtime is required.
---
## Table of contents
1. [Prerequisites](#prerequisites)
2. [Clone and first build](#clone-and-first-build)
3. [Set up MySQL and the read-only user](#set-up-mysql-and-the-read-only-user)
4. [Configure the server](#configure-the-server)
5. [Verify the server](#verify-the-server)
6. [Connect an MCP client](#connect-an-mcp-client)
- [Claude Code](#claude-code)
- [Claude Desktop](#claude-desktop)
- [Cursor IDE](#cursor-ide)
- [VS Code (Copilot Chat / Continue)](#vs-code-copilot-chat--continue)
- [puku CLI](#puku-cli)
- [Other stdio MCP clients](#other-stdio-mcp-clients)
7. [Troubleshooting](#troubleshooting)
8. [Tests, lint, build](#tests-lint-build)
9. [Privacy and logging](#privacy-and-logging)
10. [License](#license)
---
## Prerequisites
| Tool | Version | Notes |
| ------------ | ---------- | ---------------------------------------------------------------------------------------------------------------------- |
| Node.js | **22 LTS** | The repo pins `22` in `.nvmrc` and `engines.node` in `package.json`. Use `nvm use`. |
| npm | 10+ | Bundled with Node 22. |
| MySQL Server | **8.x** | Local install on `127.0.0.1:3306`. Any flavor that supports `information_schema` reads works (MySQL 8, MariaDB 10.5+). |
| Git | recent | To clone the repo. |
> **OS support:** tested on Windows 11, macOS 14, and Ubuntu 22.04. The pool
> defaults to `127.0.0.1` (TCP loopback), not the Unix socket — works
> identically across platforms.
### Required command-line tools
Make sure each of these resolves on your `PATH`:
```bash
node --version # v22.x
npm --version # 10.x
mysql --version # 8.x
git --version
```
---
## Clone and first build
```bash
# 1. Clone
git clone https://github.com/<your-org>/db-mcp-server.git
cd db-mcp-server
# 2. Use the pinned Node version
nvm install # only if you don't already have Node 22
nvm use # reads .nvmrc
# 3. Install dependencies
npm install
# 4. Sanity-check the source
npm run typecheck
npm run lint
# 5. Build the dist/ that MCP clients will invoke
npm run build
```
After this you should see a populated `dist/` directory and the file
`dist/index.js` will be the MCP server entrypoint. The shipped `.mcp.json`
points at this exact path, so any MCP client that auto-reads `.mcp.json`
(Claude Code, puku-cli, Cursor) will pick up the server as soon as
`dist/index.js` exists.
---
## Set up MySQL and the read-only user
This server connects with a **dedicated, read-only** MySQL account. It does
**not** use your root or app credentials. The bootstrap script
`scripts/setup-mysql-user.sql` creates the user `mcp_readonly`, creates the
`mavenmovies` schema if it is missing, grants `SELECT` only, and revokes
everything else.
### Step 1 — edit the SQL script
Open `scripts/setup-mysql-user.sql` and replace the two placeholders at the
top:
```sql
SET @mcp_user = 'mcp_readonly';
SET @mcp_pass = 'CHANGE_ME_BEFORE_RUNNING'; -- use a strong password
SET @mcp_schema = 'mavenmovies'; -- your schema name
```
Do **not** commit the real password. The repo's `.gitignore` already excludes
`.env`; treat `scripts/setup-mysql-user.sql` the same way if you paste a real
password into it.
### Step 2 — apply the grants
Run as a MySQL administrator (root or equivalent):
```bash
mysql -u root -p < scripts/setup-mysql-user.sql
```
### Step 3 — load a test schema (optional but recommended)
The `mavenmovies` schema is a small, free sample DB. If you don't already
have it, you can download it from the official MySQL sample and load it:
```bash
# Example: load the official mavenmovies dump.
# Replace the path with wherever you saved it.
mysql -u root -p mavenmovies < /path/to/mavenmovies.sql
```
You can use any schema; just set `MYSQL_DATABASE` and `MCP_ALLOWED_SCHEMAS`
in `.env` to match.
### Step 4 — verify the user can read
```bash
mysql -u mcp_readonly -p -h 127.0.0.1 -e "SELECT COUNT(*) FROM mavenmovies.actor;"
```
You should see a count. The same connection should **fail** for any write:
```bash
mysql -u mcp_readonly -p -h 127.0.0.1 -e "DELETE FROM mavenmovies.actor WHERE 1=0;"
# ERROR 1142 (42000): DELETE command denied to user 'mcp_readonly'@'...' for table 'actor'
```
If the write succeeds, the grants were not applied correctly — re-run
`scripts/setup-mysql-user.sql` after fixing it.
---
## Configure the server
The server reads configuration in two layers, both of which you should set
up:
### Layer 1 — `.env` (secrets and connection details)
Copy the example file and edit the secrets:
```bash
cp .env.example .env
```
Open `.env` and fill in at least:
```ini
MYSQL_HOST=127.0.0.1
MYSQL_PORT=3306
MYSQL_DATABASE=mavenmovies
MYSQL_USER=mcp_readonly
MYSQL_PASSWORD=<the password you set in step 1>
MCP_ALLOWED_SCHEMAS=mavenmovies
```
`.env` is gitignored, so this is the right place for the MySQL password.
The complete reference (every variable, defaults, validation rules, and
safety overrides) lives in [docs/CONFIGURATION.md](./docs/CONFIGURATION.md).
### Layer 2 — `.mcp.json` (MCP client wiring, committed)
A `.mcp.json` is checked into the repo root. It registers the server under
the name `mysql-db` and points at the built entry point `dist/index.js`
with a relative path. The env block lists non-secret configuration
(loopback host, schema, policy, row cap, log level) and intentionally
**omits** `MYSQL_PASSWORD` — the server picks that up from `.env`.
Most MCP clients (Claude Code, puku-cli, Cursor) read `.mcp.json`
automatically. For clients that read a different config file, see
[Connect an MCP client](#connect-an-mcp-client) below.
The full `.env.example`:
```ini
# ---- MySQL connection ----
MYSQL_HOST=127.0.0.1
MYSQL_PORT=3306
MYSQL_DATABASE=example_db
MYSQL_USER=
MYSQL_PASSWORD=
# ---- Pool / timeouts (all bounded) ----
MYSQL_CONNECTION_LIMIT=5
MYSQL_CONNECT_TIMEOUT_MS=5000
MYSQL_QUERY_TIMEOUT_MS=3000
# ---- MCP policy ----
MCP_ALLOWED_SCHEMAS=mavenmovies
MCP_ALLOWED_TABLES=
MCP_MAX_ROWS=1000
MCP_AUDIT_LOG=
# ---- Logging ----
LOG_LEVEL=info
# ---- Safety overrides ----
ALLOW_REMOTE_MYSQL=0
ALLOW_WILDCARD_SCHEMAS=0
```
---
## Verify the server
The server speaks stdio. There is no HTTP listener. To verify it boots and
connects to MySQL without an MCP client, use the included `health_check` MCP
method via any MCP client, or simply start it and watch the logs.
### Quick stdio smoke test
In one terminal, start the server in dev mode (uses `tsx`, no build needed):
```bash
npm run dev
```
You should see one JSON log line on **stderr** like:
```json
{"ts":"...","level":"info","msg":"boot","config":{"MYSQL_HOST":"127.0.0.1", ... "MYSQL_PASSWORD":"***", ...}}
{"ts":"...","level":"info","msg":"mcp.stdio.connected"}
```
The server is now waiting for an MCP JSON-RPC message on **stdin**. You can
poke it with a hand-rolled request in another terminal:
```bash
# macOS / Linux
(echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"smoke","version":"0"}}}'; sleep 1; echo '{"jsonrpc":"2.0","method":"notifications/initialized"}'; echo '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"health_check","arguments":{}}}'; sleep 1) | node dist/index.js
```
You should see a JSON-RPC response with `result.content[0].text` containing
`{"ok":true,"latencyMs":<number>}`. Press `Ctrl+C` to shut down cleanly.
A faster check is to wire it into one of the MCP clients below and call
`health_check` from there.
---
## Connect an MCP client
All clients below invoke the server as a **stdio subprocess**. They differ
only in where they store their config file and which env-var names they use.
> **Path note (Windows):** the examples below use the Windows path
> `D:/projects/db-mcp-server/dist/index.js`. On macOS/Linux substitute your
> own path (e.g. `/Users/you/code/db-mcp-server/dist/index.js`).
> The forward slashes work in JSON on every platform; do not escape them.
> **Secrets:** never commit real `MYSQL_PASSWORD` / `DB_PASSWORD` values to
> the JSON config files below. Either use a placeholder and load the real
> value from your shell environment, or use a `.env` file consumed by the
> server. The server reads `process.env`, then loads `.env` via `dotenv`
> with `override: false` (the default), so any value **already present** in
> `process.env` wins — this is why shipped client configs can omit
> `MYSQL_PASSWORD` and still authenticate against the password in `.env`.
### Claude Code
Claude Code reads MCP config from `~/.claude.json` (user-wide) or
`.mcp.json` in the project directory.
**Project-local (recommended for this repo)** — a `.mcp.json` is already
shipped at the repo root and registers the server under the name
`mysql-db` with a relative `dist/index.js` path, so no per-clone wiring is
needed. Just `npm run build`, restart Claude Code, and the server will be
loaded.
If you want to customize it (different schema, different user, etc.), edit
`.mcp.json` directly. Keep `MYSQL_PASSWORD` unset there and let `.env`
provide it — see the secrets callout above.
The shipped `.mcp.json` looks like this:
```json
{
"mcpServers": {
"mysql-db": {
"type": "stdio",
"command": "node",
"args": ["dist/index.js"],
"env": {
"MYSQL_HOST": "127.0.0.1",
"MYSQL_PORT": "3306",
"MYSQL_DATABASE": "mavenmovies",
"MYSQL_USER": "mcp_readonly",
"MCP_ALLOWED_SCHEMAS": "mavenmovies",
"MCP_MAX_ROWS": "1000",
"LOG_LEVEL": "info",
"ALLOW_REMOTE_MYSQL": "0",
"ALLOW_WILDCARD_SCHEMAS": "0"
}
}
}
}
```
> Note: `MYSQL_PASSWORD` is intentionally **absent** from the committed
> `.mcp.json`. The server picks it up from `.env` at boot. If you set
> `MYSQL_PASSWORD` in `.mcp.json` to anything (including an empty string),
> it will override `.env` and may break auth.
**User-wide** — add the same `mcpServers` block to `~/.claude.json`.
Restart Claude Code. Confirm the server is loaded with
`/mcp` — you should see `mysql-db` listed with four tools
(`list_tables`, `describe_table`, `get_rows`, `health_check`).
### Claude Desktop
Edit the Claude Desktop config file:
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Linux: `~/.config/Claude/claude_desktop_config.json`
```json
{
"mcpServers": {
"local-mysql": {
"command": "node",
"args": ["D:/projects/db-mcp-server/dist/index.js"],
"env": {
"MYSQL_HOST": "127.0.0.1",
"MYSQL_PORT": "3306",
"MYSQL_DATABASE": "mavenmovies",
"MYSQL_USER": "mcp_readonly",
"MYSQL_PASSWORD": "<set-locally>",
"MCP_ALLOWED_SCHEMAS": "mavenmovies"
}
}
}
}
```
Fully quit and reopen Claude Desktop. The hammer icon should show the four
tools.
### Cursor IDE
Create or edit `.cursor/mcp.json` in the project root:
```json
{
"mcpServers": {
"local-mysql": {
"command": "node",
"args": ["D:/projects/db-mcp-server/dist/index.js"],
"env": {
"MYSQL_HOST": "127.0.0.1",
"MYSQL_PORT": "3306",
"MYSQL_DATABASE": "mavenmovies",
"MYSQL_USER": "mcp_readonly",
"MYSQL_PASSWORD": "<set-locally>",
"MCP_ALLOWED_SCHEMAS": "mavenmovies"
}
}
}
}
```
In Cursor: `Settings → MCP → local-mysql → Refresh`. The four tools should
appear under the tools list.
### VS Code (Copilot Chat / Continue)
VS Code reads MCP config from `.vscode/mcp.json` in the workspace (Copilot
Chat and Continue both support this format).
```json
{
"servers": {
"local-mysql": {
"type": "stdio",
"command": "node",
"args": ["D:/projects/db-mcp-server/dist/index.js"],
"env": {
"MYSQL_HOST": "127.0.0.1",
"MYSQL_PORT": "3306",
"MYSQL_DATABASE": "mavenmovies",
"MYSQL_USER": "mcp_readonly",
"MYSQL_PASSWORD": "<set-locally>",
"MCP_ALLOWED_SCHEMAS": "mavenmovies"
}
}
}
}
```
For Continue (`.continue/config.json`) the same server block goes under
`mcpServers`:
```json
{
"mcpServers": [
{
"name": "local-mysql",
"command": "node",
"args": ["D:/projects/db-mcp-server/dist/index.js"],
"env": {
"MYSQL_HOST": "127.0.0.1",
"MYSQL_PORT": "3306",
"MYSQL_DATABASE": "mavenmovies",
"MYSQL_USER": "mcp_readonly",
"MYSQL_PASSWORD": "<set-locally>",
"MCP_ALLOWED_SCHEMAS": "mavenmovies"
}
}
]
}
```
Reload the VS Code window after editing.
### puku CLI
puku-cli reads MCP config from `.mcp.json` at the project root (loaded
automatically) and from `~/.puku-cli/settings.json` (global). The shipped
`.mcp.json` in this repo already registers the server as `mysql-db` — no
extra steps are required beyond `npm run build` and a session restart.
**Auto-approval note:** the first time a session launches the server, puku-cli
will prompt you to approve the MCP server it discovered in `.mcp.json`. If
you want to skip the prompt for this project, add the following to
`~/.puku-cli/settings.json`:
```json
{
"enableAllProjectMcpServers": true
}
```
Or, to approve only this one server explicitly:
```json
{
"enabledMcpjsonServers": ["mysql-db"]
}
```
For a **user-wide** install (one entry used across all your projects, with
an absolute path), add this to `~/.puku-cli/settings.json`:
```json
{
"mcpServers": {
"local-mysql": {
"type": "stdio",
"command": "node",
"args": ["D:/projects/db-mcp-server/dist/index.js"],
"env": {
"MYSQL_HOST": "127.0.0.1",
"MYSQL_PORT": "3306",
"MYSQL_DATABASE": "mavenmovies",
"MYSQL_USER": "mcp_readonly",
"MCP_ALLOWED_SCHEMAS": "mavenmovies"
}
}
}
}
```
Verify with `/mcp` inside puku-cli; you should see `mysql-db` (project
config) and/or `local-mysql` (user config) and four tools
(`list_tables`, `describe_table`, `get_rows`, `health_check`).
### Project-root-wide MCP configuration (`.mcp.json`)
A **project-root-wide** MCP configuration is the recommended way to wire
this server into any client. The repo ships a `.mcp.json` at the project
root that registers the server under the name `mysql-db` and points at the
built entrypoint `dist/index.js` with a **relative path**, so the same
config works for every contributor regardless of where they cloned the
repo.
```json
{
"mcpServers": {
"mysql-db": {
"type": "stdio",
"command": "node",
"args": ["dist/index.js"],
"env": {
"MYSQL_HOST": "127.0.0.1",
"MYSQL_PORT": "3306",
"MYSQL_DATABASE": "mavenmovies",
"MYSQL_USER": "mcp_readonly",
"MYSQL_CONNECTION_LIMIT": "5",
"MYSQL_CONNECT_TIMEOUT_MS": "5000",
"MYSQL_QUERY_TIMEOUT_MS": "3000",
"MCP_ALLOWED_SCHEMAS": "mavenmovies",
"MCP_ALLOWED_TABLES": "",
"MCP_MAX_ROWS": "1000",
"MCP_AUDIT_LOG": "",
"LOG_LEVEL": "info",
"ALLOW_REMOTE_MYSQL": "0",
"ALLOW_WILDCARD_SCHEMAS": "0"
}
}
}
}
```
#### Why project-root-wide?
- **One file, every client.** Claude Code, puku-cli, and Cursor all
auto-read `.mcp.json` from the working directory. Drop the file in the
repo root and every contributor gets the same server wiring with zero
per-machine setup.
- **Relative paths are portable.** `args: ["dist/index.js"]` resolves
against the project root, so the same JSON works on Windows, macOS,
and Linux without edits. Use absolute paths (e.g.
`D:/projects/db-mcp-server/dist/index.js`) only when you need to
launch the server from outside the project root.
- **Secrets stay out of version control.** The shipped `.mcp.json`
intentionally **omits** `MYSQL_PASSWORD`. The server reads `.env` at
boot via `dotenv` with `override: false`, so any value already present
in `process.env` wins — your `.env` provides the password and the
committed config never has to.
#### Customizing for your environment
Edit the `.mcp.json` block in place when you need to:
- **Target a different schema** — change `MYSQL_DATABASE` and
`MCP_ALLOWED_SCHEMAS` together (both must be set; the allowlist is
enforced server-side).
- **Use a different read-only user** — change `MYSQL_USER` and the
matching `MYSQL_PASSWORD` in `.env`.
- **Raise/lower the row cap** — change `MCP_MAX_ROWS`.
- **Allow a non-loopback MySQL** — change `ALLOW_REMOTE_MYSQL` to `"1"`.
The server still requires a non-empty `MYSQL_PASSWORD`.
- **Allow wildcards in `MCP_ALLOWED_SCHEMAS`** — change
`ALLOW_WILDCARD_SCHEMAS` to `"1"`. Off by default for safety.
#### Auto-loading per client
| Client | Reads `.mcp.json` automatically? | Where to place it |
| ------------- | -------------------------------- | --------------------------------------- |
| Claude Code | Yes (project-local) | repo root (`./.mcp.json`) — already there |
| puku-cli | Yes (project-local) | repo root (`./.mcp.json`) — already there |
| Cursor IDE | Yes (project-local) | repo root (`./.mcp.json`) — already there |
| Claude Desktop| No — uses `claude_desktop_config.json` | see [Claude Desktop](#claude-desktop) |
| VS Code | No — uses `.vscode/mcp.json` | see [VS Code](#vs-code-copilot-chat--continue) |
| Continue | No — uses `.continue/config.json`| see [VS Code](#vs-code-copilot-chat--continue) |
After editing `.mcp.json`, restart your client. Confirm the server is
loaded with `/mcp` — you should see `mysql-db` listed with four tools
(`list_tables`, `describe_table`, `get_rows`, `health_check`).
### Other stdio MCP clients
Any MCP client that supports stdio transport can launch the server with:
```
command: node
args: ["<absolute-path>/db-mcp-server/dist/index.js"]
env: { MYSQL_HOST, MYSQL_PORT, MYSQL_DATABASE, MYSQL_USER,
MYSQL_PASSWORD, MCP_ALLOWED_SCHEMAS, ... }
```
Use the env-var names listed in [docs/CONFIGURATION.md](./docs/CONFIGURATION.md).
The server refuses to start against non-loopback hosts unless
`ALLOW_REMOTE_MYSQL=1`, and refuses wildcards in `MCP_ALLOWED_SCHEMAS`
unless `ALLOW_WILDCARD_SCHEMAS=1`.
---
## Troubleshooting
| Symptom | Cause | Fix |
| ----------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `boot failed: Validation: Invalid configuration: MYSQL_DATABASE: ...` | `.env` missing or env not loaded | Confirm `.env` exists at the path the server reads (project root); confirm each required var is set. |
| `Validation: MYSQL_HOST=x is not loopback. Refusing to start unless ALLOW_REMOTE_MYSQL=1.` | You set `MYSQL_HOST` to a non-loopback name | Use `127.0.0.1` (recommended), or set `ALLOW_REMOTE_MYSQL=1` **and** make sure your `MYSQL_USER` grants match. |
| `Validation: Wildcards in MCP_ALLOWED_SCHEMAS require ALLOW_WILDCARD_SCHEMAS=1.` | `MCP_ALLOWED_SCHEMAS=*` (or contains `*`) | Use an explicit comma-separated list of schemas, or set `ALLOW_WILDCARD_SCHEMAS=1`. |
| `connection: ER_ACCESS_DENIED_ERROR` | Wrong user/password or user not bound to the host you're connecting from | Re-run `scripts/setup-mysql-user.sql`; verify with `mysql -u mcp_readonly -p -h 127.0.0.1 -e 'SELECT 1'`. |
| `connection: ECONNREFUSED` | MySQL not running, or wrong port | Start MySQL; verify with `mysqladmin ping -h 127.0.0.1 -P 3306`. |
| `Timeout: Query exceeded 3000ms` | Slow query, lock wait, or `MYSQL_QUERY_TIMEOUT_MS` too low | Raise `MYSQL_QUERY_TIMEOUT_MS` for ad-hoc work; check `SHOW PROCESSLIST` for blockers. |
| `Authorization: Schema 'mavenmovies' is not in the allowlist` | `MCP_ALLOWED_SCHEMAS` is empty or doesn't match | Edit `.env`, restart the server. |
| `Authorization: Column 'mavenmovies.actor.password' cannot be queried without an explicit column allowlist` | `get_rows` called without `columnAllowlist` | Pass a `columnAllowlist` listing every column you intend to read. Required, not optional. See [docs/TOOLS.md](./docs/TOOLS.md). |
| MCP client lists zero tools from `local-mysql` | Stdio path wrong, or server crashed at startup | Run `node dist/index.js` directly; you should see `mcp.stdio.connected` on stderr. Confirm the absolute path in the client config exists. |
| `Error: Cannot find module 'mysql2/promise'` | `npm install` not run, or `dist/` from before dependencies | `npm install && npm run build`. |
| MCP client shows `mysql-db` but zero tools, or doesn't show it at all after editing `.mcp.json` | Some clients only watch `.mcp.json` for changes made before the session started | Restart the client, or open `/mcp` once to force a config reload. |
| `ER_ACCESS_DENIED_ERROR` despite correct `.env` password | `.mcp.json` sets `MYSQL_PASSWORD` to an empty string, which overrides `.env` because `dotenv` does not override existing `process.env` | Remove the `MYSQL_PASSWORD` key from `.mcp.json` entirely so `.env` provides it. |
---
## Tests, lint, build
```bash
npm run typecheck # strict TypeScript
npm run lint # ESLint
npm run format:check # Prettier
npm test # unit + security + integration suites
npm run build # produces dist/
npm start # runs dist/index.js with source maps
npm run dev # tsx src/index.ts (no build step)
```
Integration tests run against the MySQL you configured in `.env`
(`MYSQL_HOST`, `MYSQL_PORT`, `MYSQL_USER`, `MYSQL_PASSWORD`). They assume
the schema and grants from `scripts/setup-mysql-user.sql` are in place —
the read-only user must be able to `SELECT` from every schema listed in
`MCP_ALLOWED_SCHEMAS`.
Run a single suite:
```bash
npm run test:unit
npm run test:integration
npm run test:security
```
---
## Privacy and logging
- Tool descriptions and error responses never include credentials, SQL
strings, or result sets beyond what each tool is designed to return.
See [docs/TOOLS.md](./docs/TOOLS.md).
- The structured logger redacts well-known secret keys (`password`,
`passwd`, `token`, `secret`, `api[_-]?key`, `authorization`,
`credential`, `access[_-]?token`) and any string containing
`password=…`. See [docs/LOGGING.md](./docs/LOGGING.md).
- Set `MCP_AUDIT_LOG=/path/to/audit.log` to write JSONL audit events per
tool call. Without it, audit events go to stderr.
- The server is **loopback-only** by default. To target a remote MySQL,
you must set `ALLOW_REMOTE_MYSQL=1` and the server still requires a
non-empty `MYSQL_PASSWORD`.
---
## License
MIT — see [`LICENSE`](./LICENSE).
TDQS
Scored across 4 tools
Each tool has a clearly distinct purpose: describe_table for schema metadata, list_tables for table enumeration, get_rows for data retrieval, and health_check for connectivity status. There is no overlap or ambiguity between them.
All tool names follow a consistent verb_noun pattern: describe_table, list_tables, get_rows, health_check. The naming is uniform and predictable.
The server has 4 tools, which is well-scoped for a read-only MySQL access layer. Each tool provides a necessary capability without redundancy or bloat.
For its stated purpose of safe, read-only access to allowlisted tables, the toolset covers the full lifecycle: listing tables, describing schema, reading rows, and verifying connectivity. No obvious gaps or dead ends exist.