🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@database-mcp/mysql

Package Overview
Dependencies
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@database-mcp/mysql - npm Package Compare versions

Comparing version
0.0.0
to
0.2.0
+14
dist/adapter.d.ts
import type { DatabaseAdapter, QueryOptions, QueryResult, TableSummary, TableDescription, Connection } from "@database-mcp/core";
export declare class MysqlAdapter implements DatabaseAdapter {
private connection;
engine: string;
private pool;
constructor(connection: Connection);
connect({ readOnly }: {
readOnly: boolean;
}): Promise<void>;
close(): Promise<void>;
query(sql: string, { maxRows, timeoutMs }: QueryOptions): Promise<QueryResult>;
listTables(): Promise<TableSummary[]>;
describeTable(table: string): Promise<TableDescription>;
}
import mysql from "mysql2/promise";
export class MysqlAdapter {
connection;
engine = "mysql";
pool;
constructor(connection) {
this.connection = connection;
}
async connect({ readOnly }) {
const c = this.connection;
const common = {
connectionLimit: 4,
// BIGINT/DECIMAL beyond JS safe range arrive as strings (JSON-safe).
supportBigNumbers: true,
bigNumberStrings: false,
};
this.pool = c.dsn
? mysql.createPool({ uri: c.dsn, ...common })
: mysql.createPool({
host: c.host ?? "127.0.0.1",
port: c.port ?? 3306,
user: c.user,
password: c.password?.reveal(),
database: c.database,
...common,
});
if (readOnly) {
// Layer two: every pooled session rejects writes at the server,
// catching what the SQL guard can't see (CTE-smuggled writes etc.).
this.pool.on("connection", (conn) => {
conn.query("SET SESSION TRANSACTION READ ONLY");
});
}
await this.pool.query("SELECT 1"); // fail fast on bad host/credentials
}
async close() {
await this.pool.end();
}
async query(sql, { maxRows, timeoutMs }) {
// ponytail: rows are fetched then capped — a SELECT over a huge table
// buffers before slicing; switch to mysql2's .stream() if it matters.
const [result, fields] = await this.pool.query({ sql, timeout: timeoutMs });
if (!Array.isArray(result)) {
return { columns: [], rows: [], rowCount: result.affectedRows ?? 0, truncated: false };
}
const columns = (fields ?? []).map((f) => f.name);
const truncated = result.length > maxRows;
const rows = result.slice(0, maxRows);
return { columns, rows, rowCount: rows.length, truncated };
}
async listTables() {
const [rows] = await this.pool.query(`SELECT TABLE_NAME AS name, TABLE_ROWS AS estimatedRows
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE()
ORDER BY TABLE_NAME`);
return rows.map((r) => ({
name: r.name,
estimatedRows: Number(r.estimatedRows ?? 0),
}));
}
async describeTable(table) {
const [cols] = await this.pool.query(`SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_KEY, COLUMN_DEFAULT
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?
ORDER BY ORDINAL_POSITION`, [table]);
const colRows = cols;
if (colRows.length === 0)
throw new Error(`unknown table: ${table}`);
const columns = colRows.map((c) => ({
name: c.COLUMN_NAME,
type: c.COLUMN_TYPE,
nullable: c.IS_NULLABLE === "YES",
key: c.COLUMN_KEY || null,
default: c.COLUMN_DEFAULT,
}));
const [idx] = await this.pool.query(`SELECT INDEX_NAME, COLUMN_NAME, NON_UNIQUE
FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?
ORDER BY INDEX_NAME, SEQ_IN_INDEX`, [table]);
const byIndex = new Map();
for (const r of idx) {
const entry = byIndex.get(r.INDEX_NAME) ?? { name: r.INDEX_NAME, columns: [], unique: !r.NON_UNIQUE };
entry.columns.push(r.COLUMN_NAME);
byIndex.set(r.INDEX_NAME, entry);
}
const [fks] = await this.pool.query(`SELECT CONSTRAINT_NAME, COLUMN_NAME, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME
FROM information_schema.KEY_COLUMN_USAGE
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?
AND REFERENCED_TABLE_NAME IS NOT NULL
ORDER BY CONSTRAINT_NAME, ORDINAL_POSITION`, [table]);
const byFk = new Map();
for (const r of fks) {
const entry = byFk.get(r.CONSTRAINT_NAME) ?? {
name: r.CONSTRAINT_NAME,
columns: [],
referencesTable: r.REFERENCED_TABLE_NAME,
referencesColumns: [],
};
entry.columns.push(r.COLUMN_NAME);
entry.referencesColumns.push(r.REFERENCED_COLUMN_NAME);
byFk.set(r.CONSTRAINT_NAME, entry);
}
return { name: table, columns, indexes: [...byIndex.values()], foreignKeys: [...byFk.values()] };
}
}
//# sourceMappingURL=adapter.js.map
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,gBAAgB,CAAC;AAanC,MAAM,OAAO,YAAY;IAIH;IAHpB,MAAM,GAAG,OAAO,CAAC;IACT,IAAI,CAAc;IAE1B,YAAoB,UAAsB;QAAtB,eAAU,GAAV,UAAU,CAAY;IAAG,CAAC;IAE9C,KAAK,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAyB;QAC/C,MAAM,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC;QAC1B,MAAM,MAAM,GAAG;YACb,eAAe,EAAE,CAAC;YAClB,qEAAqE;YACrE,iBAAiB,EAAE,IAAI;YACvB,gBAAgB,EAAE,KAAK;SACxB,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG;YACf,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,GAAG,MAAM,EAAE,CAAC;YAC7C,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC;gBACf,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,WAAW;gBAC3B,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,IAAI;gBACpB,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE;gBAC9B,QAAQ,EAAE,CAAC,CAAC,QAAQ;gBACpB,GAAG,MAAM;aACV,CAAC,CAAC;QACP,IAAI,QAAQ,EAAE,CAAC;YACb,gEAAgE;YAChE,oEAAoE;YACpE,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,YAAY,EAAE,CAAC,IAAI,EAAE,EAAE;gBAClC,IAAI,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAC;YAClD,CAAC,CAAC,CAAC;QACL,CAAC;QACD,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,oCAAoC;IACzE,CAAC;IAED,KAAK,CAAC,KAAK;QACT,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;IACxB,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,GAAW,EAAE,EAAE,OAAO,EAAE,SAAS,EAAgB;QAC3D,sEAAsE;QACtE,sEAAsE;QACtE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC;QAC5E,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3B,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,CAAC,YAAY,IAAI,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;QACzF,CAAC;QACD,MAAM,OAAO,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAClD,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,GAAG,OAAO,CAAC;QAC1C,MAAM,IAAI,GAAI,MAAoC,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;QACrE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC;IAC7D,CAAC;IAED,KAAK,CAAC,UAAU;QACd,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAClC;;;4BAGsB,CACvB,CAAC;QACF,OAAQ,IAAmD,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACtE,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC,aAAa,IAAI,CAAC,CAAC;SAC5C,CAAC,CAAC,CAAC;IACN,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,KAAa;QAC/B,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAClC;;;kCAG4B,EAC5B,CAAC,KAAK,CAAC,CACR,CAAC;QACF,MAAM,OAAO,GAAG,IAMb,CAAC;QACJ,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,KAAK,EAAE,CAAC,CAAC;QAErE,MAAM,OAAO,GAAiB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAChD,IAAI,EAAE,CAAC,CAAC,WAAW;YACnB,IAAI,EAAE,CAAC,CAAC,WAAW;YACnB,QAAQ,EAAE,CAAC,CAAC,WAAW,KAAK,KAAK;YACjC,GAAG,EAAE,CAAC,CAAC,UAAU,IAAI,IAAI;YACzB,OAAO,EAAE,CAAC,CAAC,cAAc;SAC1B,CAAC,CAAC,CAAC;QAEJ,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CACjC;;;0CAGoC,EACpC,CAAC,KAAK,CAAC,CACR,CAAC;QACF,MAAM,OAAO,GAAG,IAAI,GAAG,EAAqB,CAAC;QAC7C,KAAK,MAAM,CAAC,IAAI,GAAwE,EAAE,CAAC;YACzF,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,UAAU,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC;YACtG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;YAClC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QACnC,CAAC;QAED,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CACjC;;;;mDAI6C,EAC7C,CAAC,KAAK,CAAC,CACR,CAAC;QACF,MAAM,IAAI,GAAG,IAAI,GAAG,EAA0B,CAAC;QAC/C,KAAK,MAAM,CAAC,IAAI,GAKb,EAAE,CAAC;YACJ,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,eAAe,CAAC,IAAI;gBAC3C,IAAI,EAAE,CAAC,CAAC,eAAe;gBACvB,OAAO,EAAE,EAAE;gBACX,eAAe,EAAE,CAAC,CAAC,qBAAqB;gBACxC,iBAAiB,EAAE,EAAE;aACtB,CAAC;YACF,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;YAClC,KAAK,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC;YACvD,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,eAAe,EAAE,KAAK,CAAC,CAAC;QACrC,CAAC;QAED,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC;IACnG,CAAC;CACF"}
#!/usr/bin/env node
export {};
#!/usr/bin/env node
import { loadConfig, serve } from "@database-mcp/core";
import { createRequire } from "node:module";
import { MysqlAdapter } from "./adapter.js";
const { version } = createRequire(import.meta.url)("../package.json");
try {
const config = loadConfig(process.argv.slice(2), process.env, {
envPrefix: "MYSQL",
dsnEnvVar: "MYSQL_DSN",
});
const c = config.connection;
if (!c.dsn && !c.host && !c.database) {
throw new Error("config: no database given — pass --dsn mysql://user@host/db, set MYSQL_HOST/MYSQL_USER/MYSQL_DATABASE, or use --config");
}
await serve(new MysqlAdapter(c), config, version);
}
catch (e) {
console.error(e instanceof Error ? e.message : String(e));
process.exit(1);
}
//# sourceMappingURL=index.js.map
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,EAAE,OAAO,EAAE,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,iBAAiB,CAAwB,CAAC;AAE7F,IAAI,CAAC;IACH,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,GAAG,EAAE;QAC5D,SAAS,EAAE,OAAO;QAClB,SAAS,EAAE,WAAW;KACvB,CAAC,CAAC;IACH,MAAM,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC;IAC5B,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QACrC,MAAM,IAAI,KAAK,CACb,wHAAwH,CACzH,CAAC;IACJ,CAAC;IACD,MAAM,KAAK,CAAC,IAAI,YAAY,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;AACpD,CAAC;AAAC,OAAO,CAAC,EAAE,CAAC;IACX,OAAO,CAAC,KAAK,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC"}
+34
-4
{
"name": "@database-mcp/mysql",
"version": "0.0.0",
"description": "MCP server for MySQL with read-only guardrails, row caps, and statement timeouts. Placeholder — real release imminent.",
"version": "0.2.0",
"description": "MCP server for MySQL with read-only guardrails, row caps, and statement timeouts",
"license": "MIT",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"bin": {
"database-mcp-mysql": "dist/index.js"
},
"files": [
"dist"
],
"scripts": {
"build": "tsc"
},
"repository": {
"type": "git",
"url": "git+https://github.com/arifulislamat/database-mcp.git"
"url": "git+https://github.com/arifulislamat/database-mcp.git",
"directory": "mysql/typescript"
},
"homepage": "https://github.com/arifulislamat/database-mcp#readme",
"keywords": ["mcp", "model-context-protocol", "mysql", "database", "sql"],
"keywords": [
"mcp",
"model-context-protocol",
"mysql",
"database",
"sql"
],
"dependencies": {
"@database-mcp/core": "^0.2.0",
"mysql2": "^3.22.6"
},
"devDependencies": {
"@types/node": "^22.0.0",
"typescript": "^5.8.0"
},
"engines": {
"node": ">=20"
},
"publishConfig": {

@@ -13,0 +43,0 @@ "access": "public"

# @database-mcp/mysql
MCP server giving AI clients safe, structured access to MySQL databases.
Two tools (`execute_sql`, `search_objects`), read-only by default, row caps,
statement timeouts.
MCP server giving AI clients safe, structured access to a MySQL database.
Two tools, guardrails on by default.
**This is a placeholder release securing the package name.** The real release
follows immediately. See the
[repository](https://github.com/arifulislamat/database-mcp) for status.
## Quick start (Claude Desktop / Claude Code / Cursor)
The password comes from the environment or a mounted secret file — never
from the client config:
```json
{
"mcpServers": {
"mysql": {
"command": "npx",
"args": ["-y", "@database-mcp/mysql"],
"env": {
"MYSQL_HOST": "127.0.0.1",
"MYSQL_USER": "readonly_user",
"MYSQL_PASSWORD": "...",
"MYSQL_DATABASE": "mydb"
}
}
}
}
```
Alternatives: `MYSQL_PASSWORD_FILE=/run/secrets/mysql_password`
(Docker/K8s-style), a YAML file via `--config` (values support `${VAR}`
expansion), or `--dsn mysql://user@host:3306/db` (inline DSN passwords are
discouraged but redacted if used). `--print-config` shows the resolved
config with the password redacted.
## Tools
- **`execute_sql`** `{ sql }` — run a single SQL statement.
- **`search_objects`** `{ table? }` — list tables, or describe one (columns,
indexes, foreign keys).
## Guardrails (defaults)
| Guardrail | Default | Override |
| ------------- | -------- | ----------------------------------------- |
| Read-only | on | `--allow-write` / `ALLOW_WRITE` |
| Row cap | 1000 | `--max-rows` / `MAX_ROWS` |
| Query timeout | 30000 ms | `--query-timeout-ms` / `QUERY_TIMEOUT_MS` |
Read-only is enforced in two layers: a conservative SQL guard, plus
`SET SESSION TRANSACTION READ ONLY` on every pooled connection — so writes
smuggled through CTEs are rejected by the server itself.
## Part of database-mcp
One package per engine, identical tool contract, shared conformance suite:
[github.com/arifulislamat/database-mcp](https://github.com/arifulislamat/database-mcp)
## License
MIT