Skip to main content
Glama
andrenalin282

crispy-mysql-mcp-guard

crispy-mysql-mcp-guard

A Model Context Protocol server for MySQL (and MariaDB) where every connection decides separately which operations it allows: select, insert, update, delete and ddl.

Default is read only. Anything else has to be switched on, per connection, in plain JSON.

  • One server, any number of connections, each with its own permissions.

  • Reads run in a READ ONLY transaction, so the database itself refuses writes on the read path.

  • Exactly one statement per call. Multi-statements, executable comments (/*! ... */), INTO OUTFILE, LOAD_FILE(), LOAD DATA, GRANT, SET, CALL, USE and user/role management are rejected.

  • Result sets are capped (maxRows, default 1000) and report truncated. Large results are streamed and cut off, not loaded into memory first.

  • No password ever leaves the server: mysql_list_connections shows host, database and permissions only.

  • Configuration is JSON (command-line flags or a file). The server does not read MYSQL_* environment variables, so a project .env cannot change which database you talk to.

Quick start

No npm release yet; run it straight from GitHub (needs Node 20+):

npx -y github:andrenalin282/crispy-mysql-mcp-guard --help

Claude Code / any .mcp.json

One connection, configured inline, same shape as most MCP servers:

{
  "mcpServers": {
    "mysql-shop": {
      "type": "stdio",
      "command": "npx",
      "args": [
        "-y", "github:andrenalin282/crispy-mysql-mcp-guard",
        "--name", "shop",
        "--host", "localhost",
        "--port", "3306",
        "--user", "shop_user",
        "--password", "secret",
        "--database", "shop",
        "--allow", "select,insert,update"
      ]
    }
  }
}

--allow takes any of select,insert,update,delete,ddl. Leave it out for read only.

Several connections, one config file (keeps passwords out of .mcp.json):

{
  "mcpServers": {
    "mysql": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "github:andrenalin282/crispy-mysql-mcp-guard", "--config", "/home/me/.config/crispy-mysql-mcp-guard/config.json"]
    }
  }
}
{
  "connections": {
    "shop-live": {
      "host": "db.example.com",
      "user": "reader",
      "password": "${SHOP_LIVE_PASSWORD}",
      "database": "shop",
      "ssl": true,
      "allow": { "select": true }
    },
    "shop-dev": {
      "host": "localhost",
      "user": "root",
      "password": "dev",
      "database": "shop_dev",
      "allow": { "select": true, "insert": true, "update": true, "delete": true, "ddl": false },
      "maxRows": 500,
      "timeoutMs": 15000
    }
  }
}

The agent then picks a connection per call: mysql_query with "connection": "shop-dev". With a single connection the name can be omitted.

A full example is in mysql-mcp.example.json.

Where the config is read from

First match wins:

  1. --config <file> (relative paths are resolved against the working directory)

  2. single-connection flags (--host, --user, ...)

  3. MYSQL_MCP_GUARD_CONFIG (path to a config file)

  4. ./mysql-mcp.json

  5. ~/.config/crispy-mysql-mcp-guard/config.json

Related MCP server: MariaDB MCP Server

Connection options

Option

Default

Description

host

localhost

port

3306

user

required

password

""

Literal, or ${VAR} to read it from an environment variable (error if unset).

database

none

Default schema.

ssl

false

true verifies the server certificate, "skip-verify" encrypts without verifying.

allow.select

true

SELECT, SHOW, DESCRIBE, EXPLAIN, WITH ... SELECT

allow.insert

false

INSERT

allow.update

false

UPDATE, and the ON DUPLICATE KEY UPDATE part of an INSERT

allow.delete

false

DELETE

allow.ddl

false

CREATE, ALTER, DROP, TRUNCATE, RENAME

maxRows

1000

Maximum rows returned per query (1 to 100000).

timeoutMs

30000

Query timeout.

Command-line flags for a single connection: --name --host --port --user --password --database --ssl --allow --max-rows --timeout-ms. Unknown options, unknown operations and typos in the JSON are errors, not ignored.

Which statement needs which permission

Statement

Needs

SELECT, SHOW, DESCRIBE, EXPLAIN

select

INSERT

insert

INSERT ... ON DUPLICATE KEY UPDATE

insert + update

REPLACE (deletes a conflicting row)

insert + delete

UPDATE

update

DELETE

delete

CREATE, ALTER, DROP, TRUNCATE, RENAME

ddl

WITH ... DELETE/UPDATE/INSERT

select + the write permission

EXPLAIN ANALYZE <stmt>

whatever <stmt> needs (it executes it)

everything else

rejected

Tools

Tool

What it does

mysql_list_connections

Connections with host, database, allowed operations, limits. No passwords.

mysql_query

Runs one statement (connection, sql, optional params for ? placeholders). Returns rows, affectedRows, insertId, truncated, elapsedMs.

mysql_schema

Lists tables, or with table shows columns and indexes. Needs select.

Writes run in a transaction that is committed on success and rolled back on error. DDL commits implicitly in MySQL, as always. BIGINT values beyond 2^53 are returned as strings. Binary columns are summarized instead of dumped.

Security model, honestly

This is a guard rail for an AI agent, not a security boundary.

  • Use a database account with the least privileges you need. The allow flags are checked by the server before the statement is sent; the database account is the real limit. A connection with allow: { select: true } and a SELECT-only account cannot be talked into anything else.

  • The statement classifier is deliberately strict and refuses what it does not understand. It is not a full SQL parser, so a read can still call a stored function that writes; the READ ONLY transaction makes the server reject that on the read path.

  • CALL is not supported at all, because a stored procedure can do anything its definer can.

  • Passwords given as --password are visible in the process list and in .mcp.json. Prefer a config file with mode 600 and ${VAR} references for anything shared.

  • There is no WHERE check: DELETE FROM t is allowed if delete is on. Give the agent delete only where that is acceptable.

Development

npm install
npm run typecheck
npm test

Unit tests (SQL classifier, config) run anywhere. Integration and end-to-end tests need a MySQL or MariaDB server with a database t, an account u (all privileges on t) and an account ro (SELECT only), both with password pw:

MMG_TEST_PORT=3306 MMG_TEST_HOST=127.0.0.1 npm test

Without MMG_TEST_PORT those tests are skipped.

Compatibility: tested against MariaDB 10.11 and MySQL 8.4.

License

MIT

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables interaction with MySQL databases through MCP, supporting query execution, table operations (insert, update, delete), and schema inspection for natural language database management.
    185 npm
    MIT
  • A
    license
    B
    quality
    C
    maintenance
    Enables AI assistants to securely interact with MariaDB and MySQL databases using granular per-connection read/write permissions and transaction support. It allows users to manage multiple database connections, explore schemas, and execute controlled SQL queries through a standardized interface.
    6
    21 npm
    5
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    Enables AI agents to safely interact with MySQL/MariaDB databases, supporting read-only queries by default with optional write operations and access control.
    8
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    Enables AI agents to query and manage MySQL databases through a structured MCP interface, supporting SQL execution, table inspection, and database operations.
    9
    8 npm
    MIT