@cemalturkcann/mariadb-mcp-server 1.1.1 → 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -96,6 +96,8 @@ Example config:
96
96
 
97
97
  ## MCP Client Setup
98
98
 
99
+ On first run, a default config is created automatically at `~/.config/mariadb-mcp/config.json` (Linux/macOS) or `%APPDATA%\mariadb-mcp\config.json` (Windows). Edit it to add your connections.
100
+
99
101
  ### Claude Code
100
102
 
101
103
  Add to `~/.claude.json`:
@@ -105,10 +107,7 @@ Add to `~/.claude.json`:
105
107
  "mcpServers": {
106
108
  "mariadb": {
107
109
  "command": "npx",
108
- "args": ["-y", "@cemalturkcann/mariadb-mcp-server"],
109
- "env": {
110
- "DB_MCP_CONFIG_PATH": "/path/to/config.json"
111
- }
110
+ "args": ["-y", "@cemalturkcann/mariadb-mcp-server"]
112
111
  }
113
112
  }
114
113
  }
@@ -123,10 +122,7 @@ Add to your `opencode.json`:
123
122
  "mcp": {
124
123
  "mariadb": {
125
124
  "type": "local",
126
- "command": ["npx", "-y", "@cemalturkcann/mariadb-mcp-server"],
127
- "environment": {
128
- "DB_MCP_CONFIG_PATH": "/path/to/config.json"
129
- }
125
+ "command": ["npx", "-y", "@cemalturkcann/mariadb-mcp-server"]
130
126
  }
131
127
  }
132
128
  }
@@ -134,7 +130,7 @@ Add to your `opencode.json`:
134
130
 
135
131
  ### Other MCP Clients
136
132
 
137
- Any MCP-compatible client can use this server. The binary name is `mariadb-mcp-server` and it communicates over stdio. Point `DB_MCP_CONFIG_PATH` to your config file.
133
+ Any MCP-compatible client can use this server. The binary name is `mariadb-mcp-server` and it communicates over stdio. To use a custom config path, set `DB_MCP_CONFIG_PATH`.
138
134
 
139
135
  ## License
140
136
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cemalturkcann/mariadb-mcp-server",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "description": "MCP server for MariaDB/MySQL with per-connection read/write access control",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/src/config.js CHANGED
@@ -99,19 +99,19 @@ export function loadConfig() {
99
99
  const raw = rawConfig.connections || rawConfig.databases;
100
100
  if (!raw || typeof raw !== "object") {
101
101
  throw new Error(
102
- "config.json icinde 'connections' (veya eski format 'databases') nesnesi olmalidir.",
102
+ "config.json must contain a 'connections' (or legacy 'databases') object.",
103
103
  );
104
104
  }
105
105
 
106
106
  const entries = Object.entries(raw);
107
107
  if (entries.length === 0) {
108
- throw new Error("En az bir baglanti tanimlamalisiniz.");
108
+ throw new Error("You must define at least one connection.");
109
109
  }
110
110
 
111
111
  const connections = {};
112
112
  for (const [name, connection] of entries) {
113
113
  if (!connection || typeof connection !== "object") {
114
- throw new Error(`'${name}' baglantisi gecersiz.`);
114
+ throw new Error(`'${name}' connection is invalid.`);
115
115
  }
116
116
 
117
117
  const normalized = normalizeConnection(connection);
@@ -122,7 +122,7 @@ export function loadConfig() {
122
122
  normalized.default_row_limit > normalized.max_row_limit
123
123
  ) {
124
124
  throw new Error(
125
- `'${name}' icin default_row_limit, max_row_limit degerinden buyuk olamaz.`,
125
+ `default_row_limit for '${name}' cannot exceed max_row_limit.`,
126
126
  );
127
127
  }
128
128
 
package/src/db.js CHANGED
@@ -1,4 +1,4 @@
1
- import mariadb from "mariadb";
1
+ import { createPool } from "mariadb";
2
2
 
3
3
  export class DbManager {
4
4
  constructor(config) {
@@ -12,13 +12,13 @@ export class DbManager {
12
12
 
13
13
  getReadableConnections() {
14
14
  return this.getConnectionNames().filter(
15
- (name) => this.config.connections[name].read
15
+ (name) => this.config.connections[name].read,
16
16
  );
17
17
  }
18
18
 
19
19
  getWritableConnections() {
20
20
  return this.getConnectionNames().filter(
21
- (name) => this.config.connections[name].write
21
+ (name) => this.config.connections[name].write,
22
22
  );
23
23
  }
24
24
 
@@ -27,7 +27,7 @@ export class DbManager {
27
27
  if (!cfg) {
28
28
  const available = this.getConnectionNames().join(", ");
29
29
  throw new Error(
30
- `Baglanti bulunamadi: '${name}'. Mevcut baglantilar: ${available}`
30
+ `Connection not found: '${name}'. Available connections: ${available}`,
31
31
  );
32
32
  }
33
33
  return cfg;
@@ -59,7 +59,7 @@ export class DbManager {
59
59
  typeof c.ssl === "object" ? c.ssl : { rejectUnauthorized: false };
60
60
  }
61
61
 
62
- this.pools.set(poolKey, mariadb.createPool(poolOptions));
62
+ this.pools.set(poolKey, createPool(poolOptions));
63
63
  }
64
64
 
65
65
  return this.pools.get(poolKey);
@@ -69,7 +69,7 @@ export class DbManager {
69
69
  const cfg = this.getConnectionConfig(connectionName);
70
70
  if (!cfg.read) {
71
71
  throw new Error(
72
- `'${connectionName}' baglantisi read (okuma) izni vermiyor.`
72
+ `'${connectionName}' connection does not allow read access.`,
73
73
  );
74
74
  }
75
75
  }
@@ -78,7 +78,7 @@ export class DbManager {
78
78
  const cfg = this.getConnectionConfig(connectionName);
79
79
  if (!cfg.write) {
80
80
  throw new Error(
81
- `'${connectionName}' baglantisi write (yazma) izni vermiyor.`
81
+ `'${connectionName}' connection does not allow write access.`,
82
82
  );
83
83
  }
84
84
  }
@@ -131,10 +131,10 @@ export class DbManager {
131
131
  try {
132
132
  await conn.rollback();
133
133
  } catch {
134
- /* asil hatayi koruyoruz */
134
+ /* preserve the original error */
135
135
  }
136
136
  throw new Error(
137
- `Transaction basarisiz, rollback yapildi: ${error.message}`
137
+ `Transaction failed, rolled back: ${error.message}`,
138
138
  );
139
139
  } finally {
140
140
  conn.release();
package/src/index.js CHANGED
@@ -66,14 +66,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
66
66
 
67
67
  case "list_databases": {
68
68
  const connection = args?.connection;
69
- if (!connection) return fail("'connection' alani zorunludur.");
69
+ if (!connection) return fail("'connection' field is required.");
70
70
  const rows = await db.runReadOnly(connection, "SHOW DATABASES");
71
71
  return ok(rows);
72
72
  }
73
73
 
74
74
  case "list_tables": {
75
75
  const { connection, database } = args || {};
76
- if (!connection) return fail("'connection' alani zorunludur.");
76
+ if (!connection) return fail("'connection' field is required.");
77
77
  const sql = database
78
78
  ? `SHOW TABLES FROM \`${database}\``
79
79
  : "SHOW TABLES";
@@ -83,8 +83,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
83
83
 
84
84
  case "describe_table": {
85
85
  const { connection, table, database } = args || {};
86
- if (!connection) return fail("'connection' alani zorunludur.");
87
- if (!table) return fail("'table' alani zorunludur.");
86
+ if (!connection) return fail("'connection' field is required.");
87
+ if (!table) return fail("'table' field is required.");
88
88
  const path = database ? `\`${database}\`.\`${table}\`` : `\`${table}\``;
89
89
  const rows = await db.runReadOnly(connection, `DESCRIBE ${path}`);
90
90
  return ok(rows);
@@ -96,8 +96,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
96
96
  const database = args?.database;
97
97
  const requestedRowLimit = args?.row_limit;
98
98
 
99
- if (!connection) return fail("'connection' alani zorunludur.");
100
- if (!query) return fail("'query' alani zorunludur.");
99
+ if (!connection) return fail("'connection' field is required.");
100
+ if (!query) return fail("'query' field is required.");
101
101
 
102
102
  const connectionConfig = db.getConnectionConfig(connection);
103
103
  const validatedQuery = validateReadQuery(query);
@@ -130,8 +130,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
130
130
 
131
131
  case "execute_write": {
132
132
  const { connection, query } = args || {};
133
- if (!connection) return fail("'connection' alani zorunludur.");
134
- if (!query) return fail("'query' alani zorunludur.");
133
+ if (!connection) return fail("'connection' field is required.");
134
+ if (!query) return fail("'query' field is required.");
135
135
 
136
136
  const validatedQuery = validateWriteQuery(query);
137
137
  const meta = await db.runWrite(connection, validatedQuery);
@@ -140,9 +140,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
140
140
 
141
141
  case "execute_transaction": {
142
142
  const { connection, queries } = args || {};
143
- if (!connection) return fail("'connection' alani zorunludur.");
143
+ if (!connection) return fail("'connection' field is required.");
144
144
  if (!Array.isArray(queries) || queries.length === 0) {
145
- return fail("'queries' en az bir sorgu iceren bir dizi olmalidir.");
145
+ return fail("'queries' must be an array with at least one query.");
146
146
  }
147
147
 
148
148
  for (const q of queries) {
@@ -156,19 +156,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
156
156
  case "suggest_query": {
157
157
  const { connection, query, reason } = args || {};
158
158
  return ok({
159
- message: "MANUEL CALISTIRMA GEREKLI",
159
+ message: "MANUAL EXECUTION REQUIRED",
160
160
  connection,
161
- reason: reason || "Yazma islemi",
161
+ reason: reason || "Write operation",
162
162
  query,
163
163
  });
164
164
  }
165
165
 
166
166
  default:
167
- return fail(`Bilinmeyen arac: ${name}`);
167
+ return fail(`Unknown tool: ${name}`);
168
168
  }
169
169
  } catch (error) {
170
170
  const message = error instanceof Error ? error.message : String(error);
171
- return fail(`Hata: ${message}`);
171
+ return fail(`Error: ${message}`);
172
172
  }
173
173
  });
174
174
 
@@ -179,7 +179,7 @@ async function main() {
179
179
 
180
180
  main().catch((error) => {
181
181
  const message = error instanceof Error ? error.message : String(error);
182
- console.error(`Sunucu baslatilamadi: ${message}`);
182
+ console.error(`Server failed to start: ${message}`);
183
183
  process.exit(1);
184
184
  });
185
185
 
package/src/sqlGuard.js CHANGED
@@ -28,17 +28,17 @@ export function isWriteQuery(sql) {
28
28
 
29
29
  export function validateReadQuery(rawQuery) {
30
30
  if (typeof rawQuery !== "string") {
31
- throw new Error("Sorgu metin (string) olmalidir.");
31
+ throw new Error("Query must be a string.");
32
32
  }
33
33
 
34
34
  const query = rawQuery.trim();
35
35
  if (!query) {
36
- throw new Error("Sorgu bos olamaz.");
36
+ throw new Error("Query must not be empty.");
37
37
  }
38
38
 
39
39
  if (!isReadQuery(query)) {
40
40
  throw new Error(
41
- "Yalnizca SELECT/SHOW/DESCRIBE/EXPLAIN sorgularina izin verilir."
41
+ "Only SELECT/SHOW/DESCRIBE/EXPLAIN queries are allowed."
42
42
  );
43
43
  }
44
44
 
@@ -47,23 +47,23 @@ export function validateReadQuery(rawQuery) {
47
47
 
48
48
  export function validateWriteQuery(rawQuery) {
49
49
  if (typeof rawQuery !== "string") {
50
- throw new Error("Sorgu metin (string) olmalidir.");
50
+ throw new Error("Query must be a string.");
51
51
  }
52
52
 
53
53
  const query = rawQuery.trim();
54
54
  if (!query) {
55
- throw new Error("Sorgu bos olamaz.");
55
+ throw new Error("Query must not be empty.");
56
56
  }
57
57
 
58
58
  if (isReadQuery(query)) {
59
59
  throw new Error(
60
- "Read-only sorgular (SELECT/SHOW/DESCRIBE/EXPLAIN) execute_write'da kullanilamaz. execute_select kullanin."
60
+ "Read-only queries (SELECT/SHOW/DESCRIBE/EXPLAIN) cannot be used with execute_write. Use execute_select instead."
61
61
  );
62
62
  }
63
63
 
64
64
  if (!isWriteQuery(query)) {
65
65
  throw new Error(
66
- `Sorgu gecerli bir yazma islemi olarak taninamadi. Izin verilen: ${WRITE_PREFIXES.join(", ")}`
66
+ `Query was not recognized as a valid write operation. Allowed prefixes: ${WRITE_PREFIXES.join(", ")}`
67
67
  );
68
68
  }
69
69
 
@@ -74,9 +74,9 @@ export function validateWriteQuery(rawQuery) {
74
74
  * Resolves the effective row limit for a query.
75
75
  * Returns null when no limit should be applied.
76
76
  *
77
- * - Config'de default_row_limit / max_row_limit yoksa (0) → sinirsiz
78
- * - Kullanici row_limit verdiyse ve max_row_limit varsa → min(request, max) uygulanir
79
- * - Kullanici row_limit vermediyse ama default_row_limit varsa → default uygulanir
77
+ * - If default_row_limit / max_row_limit are absent (0) → unlimited
78
+ * - If the caller provides row_limit and max_row_limit is set → min(requested, max) is used
79
+ * - If the caller omits row_limit but default_row_limit is set → default is used
80
80
  */
81
81
  export function resolveRowLimit(requestedRowLimit, connectionConfig) {
82
82
  const defaultLimit = connectionConfig.default_row_limit || 0;
@@ -85,7 +85,7 @@ export function resolveRowLimit(requestedRowLimit, connectionConfig) {
85
85
  if (requestedRowLimit != null) {
86
86
  const n = Number(requestedRowLimit);
87
87
  if (!Number.isFinite(n) || n <= 0) {
88
- throw new Error("row_limit pozitif bir sayi olmalidir.");
88
+ throw new Error("row_limit must be a positive number.");
89
89
  }
90
90
  const chosen = Math.floor(n);
91
91
  return maxLimit > 0 ? Math.min(chosen, maxLimit) : chosen;
package/src/tools.js CHANGED
@@ -6,12 +6,12 @@ export function buildToolDefinitions(
6
6
  const tools = [
7
7
  {
8
8
  name: "list_connections",
9
- description: "Tanimli MariaDB baglantilarini listeler.",
9
+ description: "Lists configured MariaDB connections.",
10
10
  inputSchema: { type: "object", properties: {} },
11
11
  },
12
12
  {
13
13
  name: "list_databases",
14
- description: "Secilen baglantidaki veritabanlarini listeler.",
14
+ description: "Lists databases for the selected connection.",
15
15
  inputSchema: {
16
16
  type: "object",
17
17
  properties: {
@@ -22,7 +22,7 @@ export function buildToolDefinitions(
22
22
  },
23
23
  {
24
24
  name: "list_tables",
25
- description: "Secilen baglantida tablolari listeler.",
25
+ description: "Lists tables for the selected connection.",
26
26
  inputSchema: {
27
27
  type: "object",
28
28
  properties: {
@@ -34,7 +34,7 @@ export function buildToolDefinitions(
34
34
  },
35
35
  {
36
36
  name: "describe_table",
37
- description: "Bir tablonun kolon bilgilerini dondurur.",
37
+ description: "Returns column information for a table.",
38
38
  inputSchema: {
39
39
  type: "object",
40
40
  properties: {
@@ -48,7 +48,7 @@ export function buildToolDefinitions(
48
48
  {
49
49
  name: "execute_select",
50
50
  description:
51
- "Salt-okunur SELECT/SHOW/DESCRIBE/EXPLAIN sorgusu calistirir. Satir limiti uygulanir.",
51
+ "Executes a read-only SELECT/SHOW/DESCRIBE/EXPLAIN query. Row limit is enforced.",
52
52
  inputSchema: {
53
53
  type: "object",
54
54
  properties: {
@@ -66,7 +66,7 @@ export function buildToolDefinitions(
66
66
  tools.push(
67
67
  {
68
68
  name: "execute_write",
69
- description: `INSERT/UPDATE/DELETE/CREATE/ALTER/DROP vb. yazma sorgusu calistirir. Baglantilar: ${writableConnections.join(", ")}`,
69
+ description: `Executes a write query (INSERT/UPDATE/DELETE/CREATE/ALTER/DROP etc.). Connections: ${writableConnections.join(", ")}`,
70
70
  inputSchema: {
71
71
  type: "object",
72
72
  properties: {
@@ -78,7 +78,7 @@ export function buildToolDefinitions(
78
78
  },
79
79
  {
80
80
  name: "execute_transaction",
81
- description: `Birden fazla yazma sorgusunu transaction icinde calistirir. Baglantilar: ${writableConnections.join(", ")}`,
81
+ description: `Executes multiple write queries within a transaction. Connections: ${writableConnections.join(", ")}`,
82
82
  inputSchema: {
83
83
  type: "object",
84
84
  properties: {
@@ -97,7 +97,7 @@ export function buildToolDefinitions(
97
97
 
98
98
  tools.push({
99
99
  name: "suggest_query",
100
- description: "Manuel calistirilmasi gereken bir sorgu onerir.",
100
+ description: "Suggests a query that should be executed manually.",
101
101
  inputSchema: {
102
102
  type: "object",
103
103
  properties: {