Model or dataset
benborla/mcp-server-mysql avatar
benborla/mcp-server-mysql

benborla/mcp-server-mysql: a read-only MySQL bridge for Claude Code and Cursor

A Model Context Protocol server that provides read-only access to MySQL databases. This server enables LLMs to inspect database schemas and execute read-only queries.

2,140 stars253 forksJavaScriptMIT

At a glance

What is it?
An MCP server that lets Claude and other LLMs inspect MySQL schemas and run queries, read-only unless you flip specific environment flags. Here is how it installs, what the flags actually gate, and where it stops being the right tool.
Who is it for?
Adopt it if you want an LLM to read your MySQL schema and run SELECTs without giving it a write path by default, and if your client speaks stdio or HTTP MCP. Do not adopt it if you need a hosted, multi-tenant query gateway with its own audit log, or if your database is not MySQL 5.7 or newer.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 65 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The gap benborla/mcp-server-mysql fills between an LLM and a MySQL schema

An LLM asked about your production database has no schema. It will guess table names, invent columns, and write joins that look plausible and fail on execution. The usual workaround is pasting a dump or a CREATE TABLE list into the chat window, which goes stale the moment someone runs a migration and burns context on tables the question never touches.

This project is an MCP server, meaning it speaks the Model Context Protocol that Claude Code, Claude Desktop, Cursor and similar clients use to call external tools. It exposes one tool, mysql_query, and one resource, mysql://tables, which lists tables and column metadata for the connected database. The model asks for the resource when it needs to orient itself, then issues SQL through the tool. The README describes the default posture as read-only, with write operations opt-in via environment flags.

That default is the whole pitch. Plenty of database connectors exist; most of them either hand the model a connection string and hope, or require you to build a tool layer yourself. This one ships an opinion about what an LLM should be allowed to do on first connect, which is: look, do not touch. The audience is a developer running Claude Code locally against a dev or staging MySQL instance, or a small team that wants the same capability without each person wiring up their own glue.

How the read-only guarantee is actually implemented

The mechanism is environment variables checked at query dispatch. Three flags exist: ALLOW_INSERT_OPERATION, ALLOW_UPDATE_OPERATION and ALLOW_DELETE_OPERATION. The README states all write operations are disabled by default and lists the three flags as the way to enable them. There is no separate read-only user requirement imposed by the server; the gate is in the server process, not in MySQL grants.

That distinction matters more than it first appears. If the server decides what is a write, then the quality of that decision is the security boundary. The README does not document how statement classification works, whether it parses SQL, matches keywords, or relies on the mysql2 driver, and it does not describe an allowlist of statement types. So the honest reading is: the flags are a guardrail against an LLM casually issuing an UPDATE, not a hardened sandbox against a determined prompt. Anyone who wants a real boundary should create a MySQL user with SELECT-only grants and pass that user's credentials, so the database refuses writes regardless of what the server permits.

The README also advertises schema-specific permissions for per-database read and write control, PII redaction for masking sensitive data in results, SSH tunnel support, multi-DB mode, SSL/TLS with an mTLS option, and a remote mode using HTTP transport with bearer token auth. Each of those is documented in a separate file under docs/ (CONFIGURATION.md, PII-REDACTION.md) or in README-MULTI-DB.md. The main README names them without explaining them, so treat the feature list as a table of contents rather than a description.

Installing the server and running a first query in Claude Code

The README's simplest path is the Claude Code CLI, which registers the server and its environment in one command. Node.js v20 or newer is required, along with MySQL 5.7 or newer, and a MySQL user with appropriate privileges.

bash
claude mcp add mcp_server_mysql \
  -e MYSQL_HOST="127.0.0.1" \
  -e MYSQL_PORT="3306" \
  -e MYSQL_USER="root" \
  -e MYSQL_PASS="your_password" \
  -e MYSQL_DB="your_database" \
  -- npx @benborla29/mcp-server-mysql

Note that the package name on npm is @benborla29/mcp-server-mysql while the repository is benborla/mcp-server-mysql. After running this, the server appears in your MCP server list. You should not pass any ALLOW_* flag here; leaving them out is what keeps the connection read-only.

For Claude Desktop or any client that reads a JSON config, the README gives this shape. The -y flag lets npx install the package without prompting.

json
{
  "mcpServers": {
    "mcp_server_mysql": {
      "command": "npx",
      "args": ["-y", "@benborla29/mcp-server-mysql"],
      "env": {
        "MYSQL_HOST": "127.0.0.1",
        "MYSQL_PORT": "3306",
        "MYSQL_USER": "root",
        "MYSQL_PASS": "your_password",
        "MYSQL_DB": "your_database"
      }
    }
  }
}

Once connected, the first useful call is not a query at all. Ask the model to read the mysql://tables resource, which returns the table list and column metadata for the connected database. That gives it real column names before it writes any SQL, which is the difference between a working join and a hallucinated one. The README does not give a worked example of a query and its output, so expect to learn the response shape by running one.

If you prefer to build from source rather than run the published package, the contributing section gives the sequence: git clone the repository, then pnpm install, pnpm run build and pnpm test. The package.json shows the binary is exposed as mcp-server-mysql pointing at dist/index.js, and the Dockerfile builds on node:22-alpine with a two-stage pnpm install. The Dockerfile sets ALLOW_INSERT_OPERATION and ALLOW_UPDATE_OPERATION to true while leaving ALLOW_DELETE_OPERATION false, which is worth reading carefully before you deploy that image as-is.

Where the design gets thin: permissions, tunnels and the write flags

The per-database read/write control is the feature most likely to be misread. The README lists it as a key feature but does not show the configuration syntax; that lives in docs/CONFIGURATION.md, which is not reproduced here. Until you read that file, you cannot know whether schema permissions override the global ALLOW_* flags, sit alongside them, or apply only in multi-DB mode. Do not assume the global flags are a master switch that a per-schema rule cannot loosen.

The same applies to PII redaction. The README says it masks sensitive data in results automatically, and points at docs/PII-REDACTION.md. What it does not say in the main file is how columns are identified as sensitive: by name pattern, by a configured list, or by data inspection. That is the difference between redaction that covers a phone_number column and redaction that misses a column called contact. If your compliance story depends on this, the main README will not settle it.

SSH tunnel support and remote mode together describe a deployment where the MCP server does not run on the same host as the database and may not run on the same host as the client either. Remote mode uses HTTP transport with bearer token auth, so the token becomes a credential worth rotating and storing properly. The README does not describe token issuance, expiry or revocation. A bearer token with no documented rotation story is a static secret.

Finally, the write flags are coarse. There is no ALLOW_TRUNCATE_OPERATION, no statement-level allowlist, and no documented dry-run mode. If you enable UPDATE, you have enabled UPDATE, including the one with a missing WHERE clause. The README does not document rollback, transaction wrapping or a confirmation step before writes execute.

How it compares with pointing an agent at a general SQL tool

The closest alternative in practice is not another MCP server but a general-purpose database client that an agent drives, such as a CLI like the mysql client wrapped in a shell tool, or a Python connector exposed through a custom tool definition. The difference is in where the policy lives.

With a raw mysql client, the model composes a shell command and the only thing standing between it and a DROP TABLE is the prompt and the MySQL grants. There is no server-side flag to unset, because there is no server. That is simpler to set up and strictly less structured: no resource for schema discovery, no single tool with a documented name, and no consistent place to configure per-database permissions or result masking.

This project inverts that. You get a fixed surface (one tool, one resource), a documented set of environment variables, and a default that blocks writes. You give up the ability to run arbitrary client commands, and you take on a Node.js process and an MCP client that has to support the protocol. If your client does not speak MCP, this server is not usable at all, whereas a shell-wrapped mysql client works with anything that can run a command.

A second comparison worth naming: running the queries yourself and pasting results. That has no install cost, no token exposure and no chance of an errant write, but it does not scale past a handful of questions per session and it puts stale schema in the context window. The MCP server exists precisely to remove that copy-paste loop. If your usage is two questions a week, the loop is cheaper than the setup.

Maintenance, licence and what upgrading costs you

The repository is not archived. Its last push was on 2026-07-27, and the most recent release listed is v2.0.9 on 2026-06-19, following v2.0.8 on 2026-01-27 and v2.0.7 on 2025-11-18. That is a release cadence of roughly two per year across the releases shown, with the most recent one a couple of months before the last push. The project is maintained, but not on a fast cycle, and the gap between v2.0.8 and v2.0.9 is about five months. Plan for a dependency you update deliberately rather than continuously.

The licence is MIT, per both the README and the license field in package.json. MIT is permissive: you can use, modify and redistribute it, including in commercial and closed-source settings, provided the copyright notice and permission notice are preserved. That is a summary of the licence text, not legal advice; if you are embedding this in a product you ship, have your own counsel read LICENSE.md rather than this paragraph.

Upgrade cost is dominated by the MCP SDK pin. The package.json lists @modelcontextprotocol/sdk at exactly 1.15.1, not a caret range. That means the protocol library does not move until the maintainer moves it, which is good for reproducibility and bad for picking up protocol fixes without a version bump. The other dependencies (mysql2 ^3.14.1, express ^5.1.0, dotenv ^16.5.0) use ranges and will drift on a fresh install. If you run it via npx, you are pulling whatever the current published version resolves to at launch time, which is the opposite of pinned. For anything shared, install it into an image or a lockfile rather than relying on npx at runtime. The Dockerfile's two-stage build with pnpm install --prod --ignore-scripts is the pattern the repository itself ships for that.

Editorial conclusion

Adopt it if you want an LLM to read your MySQL schema and run SELECTs without giving it a write path by default, and if your client speaks stdio or HTTP MCP. Do not adopt it if you need a hosted, multi-tenant query gateway with its own audit log, or if your database is not MySQL 5.7 or newer. Before rolling it out beyond one machine, verify the three ALLOW_* flags are unset in your actual environment, that the MySQL user you pass in is itself scoped to SELECT, and that your client is reading the same .mcp.json you edited.

Frequently asked questions

Does MySQL have an MCP server?

MySQL itself does not ship one. benborla/mcp-server-mysql is a third-party Model Context Protocol server that connects to MySQL and exposes schema inspection and query execution to LLM clients. It is distributed on npm as @benborla29/mcp-server-mysql and licensed under MIT.

Can an MCP server be used with a database?

Yes. This server is exactly that case: it connects to MySQL using the mysql2 driver and exposes one tool, mysql_query, plus a mysql://tables resource that lists tables and column metadata. Read operations work by default, and write operations require explicit environment flags.

How can I use benborla/mcp-server-mysql with Claude Code?

The README's simplest path is the claude mcp add command with -e flags for MYSQL_HOST, MYSQL_PORT, MYSQL_USER, MYSQL_PASS and MYSQL_DB, ending with npx @benborla29/mcp-server-mysql. Leave the ALLOW_INSERT_OPERATION, ALLOW_UPDATE_OPERATION and ALLOW_DELETE_OPERATION variables unset to keep the connection read-only.

What is the purpose of an MCP server like mcp-server-mysql?

It gives an LLM client a defined way to reach an external system. Here that means letting the model read MySQL table and column metadata and run SQL through a named tool, instead of the user pasting schema dumps into the conversation. The README frames the default as read-only, with writes opt-in.

Official sources

  1. benborla/mcp-server-mysql on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/benborla-mcp-server-mysql.svg)](https://hysenlabs.com/projects/benborla-mcp-server-mysql)