@achmadya-dev/mcp-mysql-query 0.2.0
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/LICENSE +21 -0
- package/README.md +129 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/mysql/config.d.ts +14 -0
- package/dist/mysql/config.d.ts.map +1 -0
- package/dist/mysql/config.js +14 -0
- package/dist/mysql/config.js.map +1 -0
- package/dist/mysql/helpers.d.ts +17 -0
- package/dist/mysql/helpers.d.ts.map +1 -0
- package/dist/mysql/helpers.js +193 -0
- package/dist/mysql/helpers.js.map +1 -0
- package/dist/mysql/mysql.d.ts +16 -0
- package/dist/mysql/mysql.d.ts.map +1 -0
- package/dist/mysql/mysql.js +55 -0
- package/dist/mysql/mysql.js.map +1 -0
- package/dist/mysql/schema.d.ts +33 -0
- package/dist/mysql/schema.d.ts.map +1 -0
- package/dist/mysql/schema.js +36 -0
- package/dist/mysql/schema.js.map +1 -0
- package/dist/tools/mysql_ddl.d.ts +2 -0
- package/dist/tools/mysql_ddl.d.ts.map +1 -0
- package/dist/tools/mysql_ddl.js +31 -0
- package/dist/tools/mysql_ddl.js.map +1 -0
- package/dist/tools/mysql_delete.d.ts +2 -0
- package/dist/tools/mysql_delete.d.ts.map +1 -0
- package/dist/tools/mysql_delete.js +23 -0
- package/dist/tools/mysql_delete.js.map +1 -0
- package/dist/tools/mysql_insert.d.ts +2 -0
- package/dist/tools/mysql_insert.d.ts.map +1 -0
- package/dist/tools/mysql_insert.js +23 -0
- package/dist/tools/mysql_insert.js.map +1 -0
- package/dist/tools/mysql_select.d.ts +2 -0
- package/dist/tools/mysql_select.d.ts.map +1 -0
- package/dist/tools/mysql_select.js +19 -0
- package/dist/tools/mysql_select.js.map +1 -0
- package/dist/tools/mysql_update.d.ts +2 -0
- package/dist/tools/mysql_update.d.ts.map +1 -0
- package/dist/tools/mysql_update.js +23 -0
- package/dist/tools/mysql_update.js.map +1 -0
- package/package.json +54 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 achmadya
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# @achmadya-dev/mcp-mysql-query
|
|
2
|
+
|
|
3
|
+
MCP server for MySQL. Runs a single SQL statement per tool call over **stdio**. **Read-only by default** — writes and DDL require explicit env flags.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
- Node.js **≥ 20**
|
|
8
|
+
- A reachable MySQL server (local, Docker, or remote)
|
|
9
|
+
|
|
10
|
+
## Install from npm
|
|
11
|
+
|
|
12
|
+
Add to Cursor **Settings → MCP** or `.cursor/mcp.json`:
|
|
13
|
+
|
|
14
|
+
```json
|
|
15
|
+
{
|
|
16
|
+
"mcpServers": {
|
|
17
|
+
"mysql": {
|
|
18
|
+
"command": "npx",
|
|
19
|
+
"args": ["-y", "@achmadya-dev/mcp-mysql-query"],
|
|
20
|
+
"env": {
|
|
21
|
+
"MYSQL_HOST": "localhost",
|
|
22
|
+
"MYSQL_PORT": "3306",
|
|
23
|
+
"MYSQL_USER": "your_user",
|
|
24
|
+
"MYSQL_PASSWORD": "your_password",
|
|
25
|
+
"MYSQL_DATABASE": "your_database"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Or load credentials from a file (Cursor `envFile`):
|
|
33
|
+
|
|
34
|
+
```json
|
|
35
|
+
{
|
|
36
|
+
"mcpServers": {
|
|
37
|
+
"mysql": {
|
|
38
|
+
"command": "npx",
|
|
39
|
+
"args": ["-y", "@achmadya-dev/mcp-mysql-query"],
|
|
40
|
+
"envFile": "/absolute/path/to/.env"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Develop from source
|
|
47
|
+
|
|
48
|
+
From the repository root ([`achmadya-dev/mcp`](https://github.com/achmadya-dev/mcp)):
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
cp .env.example .env
|
|
52
|
+
pnpm install
|
|
53
|
+
docker compose up -d mysql
|
|
54
|
+
pnpm --filter @achmadya-dev/mcp-mysql-query run build
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Register the built server in `.cursor/mcp.json`:
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"mcpServers": {
|
|
62
|
+
"mysql": {
|
|
63
|
+
"command": "node",
|
|
64
|
+
"args": ["${workspaceFolder}/packages/mcp-mysql-query/dist/index.js"],
|
|
65
|
+
"envFile": "${workspaceFolder}/.env"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Relevant `.env` keys (defaults match `docker-compose.yml`):
|
|
72
|
+
|
|
73
|
+
```env
|
|
74
|
+
MYSQL_HOST=localhost
|
|
75
|
+
MYSQL_PORT=3306
|
|
76
|
+
MYSQL_USER=dev
|
|
77
|
+
MYSQL_PASSWORD=devpassword
|
|
78
|
+
MYSQL_DATABASE=devdb
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Environment variables
|
|
82
|
+
|
|
83
|
+
### Connection
|
|
84
|
+
|
|
85
|
+
| Variable | Default | Description |
|
|
86
|
+
| ---------------- | ------------ | ------------------------------- |
|
|
87
|
+
| `MYSQL_HOST` | `localhost` | MySQL host |
|
|
88
|
+
| `MYSQL_PORT` | `3306` | Port |
|
|
89
|
+
| `MYSQL_USER` | _(empty)_ | Username |
|
|
90
|
+
| `MYSQL_PASSWORD` | _(empty)_ | Password |
|
|
91
|
+
| `MYSQL_DATABASE` | _(optional)_ | Database selected after connect |
|
|
92
|
+
| `MYSQL_MAX_ROWS` | `500` | Max rows returned for `SELECT` |
|
|
93
|
+
|
|
94
|
+
### Write access
|
|
95
|
+
|
|
96
|
+
Enabled when the value is `true`, `1`, `yes`, or `on` (case-insensitive). If unset, that operation type is **rejected**.
|
|
97
|
+
|
|
98
|
+
| Variable | Allows |
|
|
99
|
+
| ------------------------ | ---------------------------------- |
|
|
100
|
+
| `ALLOW_INSERT_OPERATION` | `INSERT`, `REPLACE` |
|
|
101
|
+
| `ALLOW_UPDATE_OPERATION` | `UPDATE` |
|
|
102
|
+
| `ALLOW_DELETE_OPERATION` | `DELETE` |
|
|
103
|
+
| `ALLOW_DDL_OPERATION` | DDL (`CREATE`, `ALTER`, `DROP`, …) |
|
|
104
|
+
|
|
105
|
+
## Tools
|
|
106
|
+
|
|
107
|
+
| Tool | Statements | Env flag |
|
|
108
|
+
| -------------- | ----------------------------------------------- | ------------------------ |
|
|
109
|
+
| `mysql_select` | `SELECT`, `SHOW`, `DESCRIBE`, `EXPLAIN`, `DESC` | always on |
|
|
110
|
+
| `mysql_insert` | `INSERT`, `REPLACE` | `ALLOW_INSERT_OPERATION` |
|
|
111
|
+
| `mysql_update` | `UPDATE` | `ALLOW_UPDATE_OPERATION` |
|
|
112
|
+
| `mysql_delete` | `DELETE` | `ALLOW_DELETE_OPERATION` |
|
|
113
|
+
| `mysql_ddl` | DDL | `ALLOW_DDL_OPERATION` |
|
|
114
|
+
|
|
115
|
+
Each tool accepts one `sql` string. Results are JSON text; `SELECT` output is capped by `MYSQL_MAX_ROWS`.
|
|
116
|
+
|
|
117
|
+
## Behavior and security
|
|
118
|
+
|
|
119
|
+
- One SQL statement per request (no `;`-separated batches).
|
|
120
|
+
- Dangerous patterns are blocked regardless of flags.
|
|
121
|
+
- Read operations are always permitted.
|
|
122
|
+
|
|
123
|
+
## Package scripts
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
pnpm run build
|
|
127
|
+
pnpm test
|
|
128
|
+
pnpm start
|
|
129
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { startMcpServer } from "@achmadya-dev/mcp-core";
|
|
3
|
+
import packageJson from "../package.json" with { type: "json" };
|
|
4
|
+
import { mysql_ddl } from "./tools/mysql_ddl.js";
|
|
5
|
+
import { mysql_delete } from "./tools/mysql_delete.js";
|
|
6
|
+
import { mysql_insert } from "./tools/mysql_insert.js";
|
|
7
|
+
import { mysql_select } from "./tools/mysql_select.js";
|
|
8
|
+
import { mysql_update } from "./tools/mysql_update.js";
|
|
9
|
+
await startMcpServer({
|
|
10
|
+
name: "MySQL Database",
|
|
11
|
+
version: packageJson.version,
|
|
12
|
+
tools: [mysql_select, mysql_insert, mysql_update, mysql_delete, mysql_ddl],
|
|
13
|
+
});
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,WAAW,MAAM,iBAAiB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAChE,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAEvD,MAAM,cAAc,CAAC;IACnB,IAAI,EAAE,gBAAgB;IACtB,OAAO,EAAE,WAAW,CAAC,OAAO;IAC5B,KAAK,EAAE,CAAC,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,CAAC;CAC3E,CAAC,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
host: string;
|
|
3
|
+
port: number;
|
|
4
|
+
user: string;
|
|
5
|
+
password: string;
|
|
6
|
+
database: string | undefined;
|
|
7
|
+
maxRows: number;
|
|
8
|
+
allowInsert: boolean;
|
|
9
|
+
allowUpdate: boolean;
|
|
10
|
+
allowDelete: boolean;
|
|
11
|
+
allowDdl: boolean;
|
|
12
|
+
};
|
|
13
|
+
export default _default;
|
|
14
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/mysql/config.ts"],"names":[],"mappings":";;;;;;;;;;;;AAEA,wBAWE"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { envBool, envInt, envStr } from "@achmadya-dev/mcp-core";
|
|
2
|
+
export default {
|
|
3
|
+
host: envStr("MYSQL_HOST", "localhost"),
|
|
4
|
+
port: envInt("MYSQL_PORT", 3306),
|
|
5
|
+
user: envStr("MYSQL_USER"),
|
|
6
|
+
password: envStr("MYSQL_PASSWORD", ""),
|
|
7
|
+
database: envStr("MYSQL_DATABASE") || undefined,
|
|
8
|
+
maxRows: envInt("MYSQL_MAX_ROWS", 500),
|
|
9
|
+
allowInsert: envBool("ALLOW_INSERT_OPERATION"),
|
|
10
|
+
allowUpdate: envBool("ALLOW_UPDATE_OPERATION"),
|
|
11
|
+
allowDelete: envBool("ALLOW_DELETE_OPERATION"),
|
|
12
|
+
allowDdl: envBool("ALLOW_DDL_OPERATION"),
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/mysql/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAEjE,eAAe;IACb,IAAI,EAAE,MAAM,CAAC,YAAY,EAAE,WAAW,CAAC;IACvC,IAAI,EAAE,MAAM,CAAC,YAAY,EAAE,IAAI,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC,YAAY,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC,gBAAgB,EAAE,EAAE,CAAC;IACtC,QAAQ,EAAE,MAAM,CAAC,gBAAgB,CAAC,IAAI,SAAS;IAC/C,OAAO,EAAE,MAAM,CAAC,gBAAgB,EAAE,GAAG,CAAC;IACtC,WAAW,EAAE,OAAO,CAAC,wBAAwB,CAAC;IAC9C,WAAW,EAAE,OAAO,CAAC,wBAAwB,CAAC;IAC9C,WAAW,EAAE,OAAO,CAAC,wBAAwB,CAAC;IAC9C,QAAQ,EAAE,OAAO,CAAC,qBAAqB,CAAC;CACzC,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate and clean up input SQL query and allowed prefixes.
|
|
3
|
+
*/
|
|
4
|
+
export declare function validateInputs(sql: string, allowedPrefixes: string[]): {
|
|
5
|
+
cleanSql: string;
|
|
6
|
+
prefixes: string[];
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Splits the SQL query, parses syntax, handles quoting/nesting/comments,
|
|
10
|
+
* and extracts the single SQL statement while ensuring syntax correctness.
|
|
11
|
+
*/
|
|
12
|
+
export declare function parseSingleStatement(sql: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Validates the statement prefix and detects dangerous SQL patterns (hardening).
|
|
15
|
+
*/
|
|
16
|
+
export declare function validateStatement(statement: string, prefixes: string[]): void;
|
|
17
|
+
//# sourceMappingURL=helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/mysql/helpers.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,EACX,eAAe,EAAE,MAAM,EAAE,GACxB;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CAY1C;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAmIxD;AAiCD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,CA+B7E"}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { ToolError } from "@achmadya-dev/mcp-core";
|
|
2
|
+
/**
|
|
3
|
+
* Validate and clean up input SQL query and allowed prefixes.
|
|
4
|
+
*/
|
|
5
|
+
export function validateInputs(sql, allowedPrefixes) {
|
|
6
|
+
const cleanSql = sql.trim();
|
|
7
|
+
if (!cleanSql) {
|
|
8
|
+
throw new ToolError("SQL query cannot be empty.");
|
|
9
|
+
}
|
|
10
|
+
if (allowedPrefixes.length === 0) {
|
|
11
|
+
throw new ToolError("allowedPrefixes cannot be empty.");
|
|
12
|
+
}
|
|
13
|
+
const prefixes = allowedPrefixes.map((p) => p.trim().toUpperCase());
|
|
14
|
+
return { cleanSql, prefixes };
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Splits the SQL query, parses syntax, handles quoting/nesting/comments,
|
|
18
|
+
* and extracts the single SQL statement while ensuring syntax correctness.
|
|
19
|
+
*/
|
|
20
|
+
export function parseSingleStatement(sql) {
|
|
21
|
+
const parts = [];
|
|
22
|
+
let current = "";
|
|
23
|
+
let inSingleQuote = false;
|
|
24
|
+
let inDoubleQuote = false;
|
|
25
|
+
let inBacktick = false;
|
|
26
|
+
let inBracketIdentifier = false;
|
|
27
|
+
let inLineComment = false;
|
|
28
|
+
let inBlockComment = false;
|
|
29
|
+
let escape = false;
|
|
30
|
+
for (let i = 0; i < sql.length; i++) {
|
|
31
|
+
const char = sql[i];
|
|
32
|
+
const next = sql[i + 1];
|
|
33
|
+
if (escape) {
|
|
34
|
+
current += char;
|
|
35
|
+
escape = false;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (char === "\\" && (inSingleQuote || inDoubleQuote || inBacktick)) {
|
|
39
|
+
current += char;
|
|
40
|
+
escape = true;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (inLineComment) {
|
|
44
|
+
current += char;
|
|
45
|
+
if (char === "\n") {
|
|
46
|
+
inLineComment = false;
|
|
47
|
+
}
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (inBlockComment) {
|
|
51
|
+
current += char;
|
|
52
|
+
if (char === "*" && next === "/") {
|
|
53
|
+
current += next;
|
|
54
|
+
i++;
|
|
55
|
+
inBlockComment = false;
|
|
56
|
+
}
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (!inSingleQuote && !inDoubleQuote && !inBacktick && !inBracketIdentifier) {
|
|
60
|
+
if (char === "-" && next === "-") {
|
|
61
|
+
current += char + next;
|
|
62
|
+
i++;
|
|
63
|
+
inLineComment = true;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (char === "/" && next === "*") {
|
|
67
|
+
current += char + next;
|
|
68
|
+
i++;
|
|
69
|
+
inBlockComment = true;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (char === "'" && !inDoubleQuote && !inBacktick && !inBracketIdentifier) {
|
|
74
|
+
if (inSingleQuote && next === "'") {
|
|
75
|
+
current += "''";
|
|
76
|
+
i++;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
inSingleQuote = !inSingleQuote;
|
|
80
|
+
current += char;
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (char === '"' && !inSingleQuote && !inBacktick && !inBracketIdentifier) {
|
|
84
|
+
if (inDoubleQuote && next === '"') {
|
|
85
|
+
current += '""';
|
|
86
|
+
i++;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
inDoubleQuote = !inDoubleQuote;
|
|
90
|
+
current += char;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (char === "`" && !inSingleQuote && !inDoubleQuote && !inBracketIdentifier) {
|
|
94
|
+
inBacktick = !inBacktick;
|
|
95
|
+
current += char;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (char === "[" && !inSingleQuote && !inDoubleQuote && !inBacktick) {
|
|
99
|
+
inBracketIdentifier = true;
|
|
100
|
+
current += char;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
if (char === "]" && inBracketIdentifier) {
|
|
104
|
+
inBracketIdentifier = false;
|
|
105
|
+
current += char;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (char === ";" && !inSingleQuote && !inDoubleQuote && !inBacktick && !inBracketIdentifier) {
|
|
109
|
+
const trimmed = current.trim();
|
|
110
|
+
if (trimmed.length > 0) {
|
|
111
|
+
parts.push(trimmed);
|
|
112
|
+
}
|
|
113
|
+
current = "";
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
current += char;
|
|
117
|
+
}
|
|
118
|
+
if (inSingleQuote)
|
|
119
|
+
throw new ToolError("Unterminated single quote string.");
|
|
120
|
+
if (inDoubleQuote)
|
|
121
|
+
throw new ToolError("Unterminated double quote identifier.");
|
|
122
|
+
if (inBacktick)
|
|
123
|
+
throw new ToolError("Unterminated backtick identifier.");
|
|
124
|
+
if (inBracketIdentifier)
|
|
125
|
+
throw new ToolError("Unterminated bracket identifier.");
|
|
126
|
+
if (inBlockComment)
|
|
127
|
+
throw new ToolError("Unterminated block comment.");
|
|
128
|
+
const last = current.trim();
|
|
129
|
+
if (last.length > 0) {
|
|
130
|
+
parts.push(last);
|
|
131
|
+
}
|
|
132
|
+
if (parts.length !== 1) {
|
|
133
|
+
throw new ToolError("Only a single SQL query is allowed per call.");
|
|
134
|
+
}
|
|
135
|
+
return parts[0];
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Recursively strips leading single-line and block comments from a SQL statement.
|
|
139
|
+
*/
|
|
140
|
+
function stripLeadingComments(sql) {
|
|
141
|
+
let result = sql.trim();
|
|
142
|
+
while (true) {
|
|
143
|
+
if (result.startsWith("--")) {
|
|
144
|
+
const newlineIndex = result.indexOf("\n");
|
|
145
|
+
if (newlineIndex === -1) {
|
|
146
|
+
return "";
|
|
147
|
+
}
|
|
148
|
+
result = result.slice(newlineIndex + 1).trim();
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (result.startsWith("/*")) {
|
|
152
|
+
const endIndex = result.indexOf("*/");
|
|
153
|
+
if (endIndex === -1) {
|
|
154
|
+
throw new ToolError("Unterminated leading block comment.");
|
|
155
|
+
}
|
|
156
|
+
result = result.slice(endIndex + 2).trim();
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
return result;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Validates the statement prefix and detects dangerous SQL patterns (hardening).
|
|
165
|
+
*/
|
|
166
|
+
export function validateStatement(statement, prefixes) {
|
|
167
|
+
const stripped = stripLeadingComments(statement);
|
|
168
|
+
const match = stripped.match(/^([A-Z]+)/i);
|
|
169
|
+
if (!match) {
|
|
170
|
+
throw new ToolError("Unable to determine SQL statement type.");
|
|
171
|
+
}
|
|
172
|
+
const firstKeyword = match[1].toUpperCase();
|
|
173
|
+
if (!prefixes.includes(firstKeyword)) {
|
|
174
|
+
throw new ToolError(`SQL query is not allowed for this tool. Allowed statements: ${prefixes.join(", ")}`);
|
|
175
|
+
}
|
|
176
|
+
const dangerousPatterns = [
|
|
177
|
+
/\bXP_CMDSHELL\b/i,
|
|
178
|
+
/\bEXEC\b/i,
|
|
179
|
+
/\bEXECUTE\b/i,
|
|
180
|
+
/\bPREPARE\b/i,
|
|
181
|
+
/\bDEALLOCATE\b/i,
|
|
182
|
+
/\bATTACH\s+DATABASE\b/i,
|
|
183
|
+
/\bCOPY\s+.*\s+PROGRAM\b/i,
|
|
184
|
+
/\bLOAD_FILE\s*\(/i,
|
|
185
|
+
/\bINTO\s+OUTFILE\b/i,
|
|
186
|
+
];
|
|
187
|
+
for (const pattern of dangerousPatterns) {
|
|
188
|
+
if (pattern.test(statement)) {
|
|
189
|
+
throw new ToolError(`Dangerous SQL pattern detected: ${pattern}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
//# sourceMappingURL=helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../src/mysql/helpers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { RowDataPacket } from "mysql2/promise";
|
|
2
|
+
export declare function safeQuery(sql: string, allowedPrefixes: string[]): string;
|
|
3
|
+
export declare function runSql(sql: string): Promise<{
|
|
4
|
+
kind: "resultset";
|
|
5
|
+
columns: string[];
|
|
6
|
+
rowCount: number;
|
|
7
|
+
totalRows: number;
|
|
8
|
+
truncated: boolean;
|
|
9
|
+
maxRows: number;
|
|
10
|
+
rows: RowDataPacket[];
|
|
11
|
+
} | {
|
|
12
|
+
kind: "execute";
|
|
13
|
+
affectedRows: number;
|
|
14
|
+
insertId: string | null;
|
|
15
|
+
}>;
|
|
16
|
+
//# sourceMappingURL=mysql.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mysql.d.ts","sourceRoot":"","sources":["../../src/mysql/mysql.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAgC,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAIlF,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,GAAG,MAAM,CAKxE;AAED,wBAAsB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAC9C;IACE,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,aAAa,EAAE,CAAC;CACvB,GACD;IACE,IAAI,EAAE,SAAS,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,CACJ,CAwCA"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import mysql from "mysql2/promise";
|
|
2
|
+
import { ToolError } from "@achmadya-dev/mcp-core";
|
|
3
|
+
import config from "./config.js";
|
|
4
|
+
import * as helpers from "./helpers.js";
|
|
5
|
+
export function safeQuery(sql, allowedPrefixes) {
|
|
6
|
+
const { cleanSql, prefixes } = helpers.validateInputs(sql, allowedPrefixes);
|
|
7
|
+
const statement = helpers.parseSingleStatement(cleanSql);
|
|
8
|
+
helpers.validateStatement(statement, prefixes);
|
|
9
|
+
return statement;
|
|
10
|
+
}
|
|
11
|
+
export async function runSql(sql) {
|
|
12
|
+
let conn;
|
|
13
|
+
try {
|
|
14
|
+
conn = await mysql.createConnection({
|
|
15
|
+
host: config.host,
|
|
16
|
+
port: config.port,
|
|
17
|
+
user: config.user,
|
|
18
|
+
password: config.password,
|
|
19
|
+
database: config.database,
|
|
20
|
+
charset: "utf8mb4",
|
|
21
|
+
multipleStatements: false,
|
|
22
|
+
});
|
|
23
|
+
const [rows, fields] = await conn.execute(sql);
|
|
24
|
+
if (fields && Array.isArray(fields) && fields.length > 0) {
|
|
25
|
+
const columns = fields.map((f) => f.name);
|
|
26
|
+
const all = rows;
|
|
27
|
+
const truncated = all.length > config.maxRows;
|
|
28
|
+
const display = all.slice(0, config.maxRows);
|
|
29
|
+
return {
|
|
30
|
+
kind: "resultset",
|
|
31
|
+
columns,
|
|
32
|
+
rowCount: display.length,
|
|
33
|
+
totalRows: all.length,
|
|
34
|
+
truncated,
|
|
35
|
+
maxRows: config.maxRows,
|
|
36
|
+
rows: display,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
const header = rows;
|
|
40
|
+
const id = header.insertId;
|
|
41
|
+
return {
|
|
42
|
+
kind: "execute",
|
|
43
|
+
affectedRows: header.affectedRows ?? 0,
|
|
44
|
+
insertId: id != null && Number(id) > 0 ? String(id) : null,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
catch (e) {
|
|
48
|
+
throw new ToolError(`MySQL: ${e instanceof Error ? e.message : String(e)}`);
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
if (conn)
|
|
52
|
+
await conn.end();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=mysql.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mysql.js","sourceRoot":"","sources":["../../src/mysql/mysql.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,gBAAgB,CAAC;AACnC,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAEnD,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AAExC,MAAM,UAAU,SAAS,CAAC,GAAW,EAAE,eAAyB;IAC9D,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,cAAc,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;IAC5E,MAAM,SAAS,GAAG,OAAO,CAAC,oBAAoB,CAAC,QAAQ,CAAC,CAAC;IACzD,OAAO,CAAC,iBAAiB,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAC/C,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,GAAW;IAgBtC,IAAI,IAAI,CAAC;IACT,IAAI,CAAC;QACH,IAAI,GAAG,MAAM,KAAK,CAAC,gBAAgB,CAAC;YAClC,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,OAAO,EAAE,SAAS;YAClB,kBAAkB,EAAE,KAAK;SAC1B,CAAC,CAAC;QACH,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC/C,IAAI,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACzD,MAAM,OAAO,GAAI,MAAwB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC7D,MAAM,GAAG,GAAG,IAAuB,CAAC;YACpC,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC;YAC9C,MAAM,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;YAC7C,OAAO;gBACL,IAAI,EAAE,WAAW;gBACjB,OAAO;gBACP,QAAQ,EAAE,OAAO,CAAC,MAAM;gBACxB,SAAS,EAAE,GAAG,CAAC,MAAM;gBACrB,SAAS;gBACT,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,IAAI,EAAE,OAAO;aACd,CAAC;QACJ,CAAC;QACD,MAAM,MAAM,GAAG,IAAuB,CAAC;QACvC,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC;QAC3B,OAAO;YACL,IAAI,EAAE,SAAS;YACf,YAAY,EAAE,MAAM,CAAC,YAAY,IAAI,CAAC;YACtC,QAAQ,EAAE,EAAE,IAAI,IAAI,IAAI,MAAM,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI;SAC3D,CAAC;IACJ,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,IAAI,SAAS,CAAC,UAAU,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC9E,CAAC;YAAS,CAAC;QACT,IAAI,IAAI;YAAE,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const mysqlQueryInputSchema: z.ZodObject<{
|
|
3
|
+
sql: z.ZodString;
|
|
4
|
+
}, z.core.$strip>;
|
|
5
|
+
export declare const mysqlQueryOutputShape: z.ZodObject<{
|
|
6
|
+
kind: z.ZodEnum<{
|
|
7
|
+
resultset: "resultset";
|
|
8
|
+
execute: "execute";
|
|
9
|
+
}>;
|
|
10
|
+
columns: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
11
|
+
rowCount: z.ZodOptional<z.ZodNumber>;
|
|
12
|
+
totalRows: z.ZodOptional<z.ZodNumber>;
|
|
13
|
+
truncated: z.ZodOptional<z.ZodBoolean>;
|
|
14
|
+
maxRows: z.ZodOptional<z.ZodNumber>;
|
|
15
|
+
rows: z.ZodOptional<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>>>>;
|
|
16
|
+
affectedRows: z.ZodOptional<z.ZodNumber>;
|
|
17
|
+
insertId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
18
|
+
}, z.core.$strip>;
|
|
19
|
+
export declare const mysqlQueryResultSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
20
|
+
kind: z.ZodLiteral<"resultset">;
|
|
21
|
+
columns: z.ZodArray<z.ZodString>;
|
|
22
|
+
rowCount: z.ZodNumber;
|
|
23
|
+
totalRows: z.ZodNumber;
|
|
24
|
+
truncated: z.ZodBoolean;
|
|
25
|
+
maxRows: z.ZodNumber;
|
|
26
|
+
rows: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>>>;
|
|
27
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
28
|
+
kind: z.ZodLiteral<"execute">;
|
|
29
|
+
affectedRows: z.ZodNumber;
|
|
30
|
+
insertId: z.ZodNullable<z.ZodString>;
|
|
31
|
+
}, z.core.$strip>], "kind">;
|
|
32
|
+
export type MysqlQueryResult = z.infer<typeof mysqlQueryResultSchema>;
|
|
33
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/mysql/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,qBAAqB;;iBAShC,CAAC;AAEH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;iBAUhC,CAAC;AAEH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;2BAejC,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const rowValue = z.union([z.string(), z.number(), z.boolean(), z.null()]);
|
|
3
|
+
export const mysqlQueryInputSchema = z.object({
|
|
4
|
+
sql: z
|
|
5
|
+
.string()
|
|
6
|
+
.check(z.trim(), z.minLength(1, "SQL cannot be empty"), z.maxLength(100_000, "SQL is too long (max 100,000 characters)"))
|
|
7
|
+
.describe("A single SQL statement. Multiple statements separated by ';' are not allowed."),
|
|
8
|
+
});
|
|
9
|
+
export const mysqlQueryOutputShape = z.object({
|
|
10
|
+
kind: z.enum(["resultset", "execute"]),
|
|
11
|
+
columns: z.array(z.string()).optional(),
|
|
12
|
+
rowCount: z.number().int().nonnegative().optional(),
|
|
13
|
+
totalRows: z.number().int().nonnegative().optional(),
|
|
14
|
+
truncated: z.boolean().optional(),
|
|
15
|
+
maxRows: z.number().int().positive().optional(),
|
|
16
|
+
rows: z.array(z.record(z.string(), rowValue)).optional(),
|
|
17
|
+
affectedRows: z.number().int().nonnegative().optional(),
|
|
18
|
+
insertId: z.string().nullable().optional(),
|
|
19
|
+
});
|
|
20
|
+
export const mysqlQueryResultSchema = z.discriminatedUnion("kind", [
|
|
21
|
+
z.object({
|
|
22
|
+
kind: z.literal("resultset"),
|
|
23
|
+
columns: z.array(z.string()),
|
|
24
|
+
rowCount: z.number().int().nonnegative(),
|
|
25
|
+
totalRows: z.number().int().nonnegative(),
|
|
26
|
+
truncated: z.boolean(),
|
|
27
|
+
maxRows: z.number().int().positive(),
|
|
28
|
+
rows: z.array(z.record(z.string(), rowValue)),
|
|
29
|
+
}),
|
|
30
|
+
z.object({
|
|
31
|
+
kind: z.literal("execute"),
|
|
32
|
+
affectedRows: z.number().int().nonnegative(),
|
|
33
|
+
insertId: z.string().nullable(),
|
|
34
|
+
}),
|
|
35
|
+
]);
|
|
36
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/mysql/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;AAE1E,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,GAAG,EAAE,CAAC;SACH,MAAM,EAAE;SACR,KAAK,CACJ,CAAC,CAAC,IAAI,EAAE,EACR,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,qBAAqB,CAAC,EACrC,CAAC,CAAC,SAAS,CAAC,OAAO,EAAE,0CAA0C,CAAC,CACjE;SACA,QAAQ,CAAC,+EAA+E,CAAC;CAC7F,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;IACtC,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IACvC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE;IACnD,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE;IACpD,SAAS,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IACjC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC/C,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,EAAE;IACxD,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE;IACvD,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;CAC3C,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,kBAAkB,CAAC,MAAM,EAAE;IACjE,CAAC,CAAC,MAAM,CAAC;QACP,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC;QAC5B,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;QAC5B,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;QACxC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;QACzC,SAAS,EAAE,CAAC,CAAC,OAAO,EAAE;QACtB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;QACpC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,QAAQ,CAAC,CAAC;KAC9C,CAAC;IACF,CAAC,CAAC,MAAM,CAAC;QACP,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC;QAC1B,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;QAC5C,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;KAChC,CAAC;CACH,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mysql_ddl.d.ts","sourceRoot":"","sources":["../../src/tools/mysql_ddl.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,SAAS,mDA0BpB,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { defineTool, ToolError } from "@achmadya-dev/mcp-core";
|
|
2
|
+
import { runSql, safeQuery } from "../mysql/mysql.js";
|
|
3
|
+
import { mysqlQueryInputSchema, mysqlQueryOutputShape, mysqlQueryResultSchema, } from "../mysql/schema.js";
|
|
4
|
+
import config from "../mysql/config.js";
|
|
5
|
+
export const mysql_ddl = defineTool({
|
|
6
|
+
name: "mysql_ddl",
|
|
7
|
+
description: "Modify the database schema or permissions using CREATE, ALTER, DROP, TRUNCATE, RENAME, GRANT, or REVOKE. Only a single query is allowed. If the operation is rejected as not allowed, you must respect this safety restriction and do not attempt to bypass it via terminal commands, custom scripts, or external tools.",
|
|
8
|
+
inputSchema: mysqlQueryInputSchema,
|
|
9
|
+
outputSchema: mysqlQueryOutputShape,
|
|
10
|
+
handler: async ({ sql }) => {
|
|
11
|
+
if (!config.allowDdl) {
|
|
12
|
+
throw new ToolError("DDL operation is not allowed on this server.");
|
|
13
|
+
}
|
|
14
|
+
const query = safeQuery(sql, [
|
|
15
|
+
"CREATE",
|
|
16
|
+
"ALTER",
|
|
17
|
+
"DROP",
|
|
18
|
+
"TRUNCATE",
|
|
19
|
+
"RENAME",
|
|
20
|
+
"GRANT",
|
|
21
|
+
"REVOKE",
|
|
22
|
+
]);
|
|
23
|
+
const result = await runSql(query);
|
|
24
|
+
const parsed = mysqlQueryResultSchema.safeParse(result);
|
|
25
|
+
if (!parsed.success) {
|
|
26
|
+
throw new ToolError(`Invalid query result: ${parsed.error.message}`);
|
|
27
|
+
}
|
|
28
|
+
return parsed.data;
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
//# sourceMappingURL=mysql_ddl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mysql_ddl.js","sourceRoot":"","sources":["../../src/tools/mysql_ddl.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAE/D,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,MAAM,MAAM,oBAAoB,CAAC;AAExC,MAAM,CAAC,MAAM,SAAS,GAAG,UAAU,CAAC;IAClC,IAAI,EAAE,WAAW;IACjB,WAAW,EACT,0TAA0T;IAC5T,WAAW,EAAE,qBAAqB;IAClC,YAAY,EAAE,qBAAqB;IACnC,OAAO,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;QACzB,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;YACrB,MAAM,IAAI,SAAS,CAAC,8CAA8C,CAAC,CAAC;QACtE,CAAC;QACD,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,EAAE;YAC3B,QAAQ;YACR,OAAO;YACP,MAAM;YACN,UAAU;YACV,QAAQ;YACR,OAAO;YACP,QAAQ;SACT,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,CAAC;QACnC,MAAM,MAAM,GAAG,sBAAsB,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACxD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,MAAM,IAAI,SAAS,CAAC,yBAAyB,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACvE,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mysql_delete.d.ts","sourceRoot":"","sources":["../../src/tools/mysql_delete.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,YAAY,mDAkBvB,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { defineTool, ToolError } from "@achmadya-dev/mcp-core";
|
|
2
|
+
import { runSql, safeQuery } from "../mysql/mysql.js";
|
|
3
|
+
import { mysqlQueryInputSchema, mysqlQueryOutputShape, mysqlQueryResultSchema, } from "../mysql/schema.js";
|
|
4
|
+
import config from "../mysql/config.js";
|
|
5
|
+
export const mysql_delete = defineTool({
|
|
6
|
+
name: "mysql_delete",
|
|
7
|
+
description: "Delete data from the database using DELETE. Only a single query is allowed. If the operation is rejected as not allowed, you must respect this safety restriction and do not attempt to bypass it via terminal commands, custom scripts, or external tools.",
|
|
8
|
+
inputSchema: mysqlQueryInputSchema,
|
|
9
|
+
outputSchema: mysqlQueryOutputShape,
|
|
10
|
+
handler: async ({ sql }) => {
|
|
11
|
+
if (!config.allowDelete) {
|
|
12
|
+
throw new ToolError("DELETE operation is not allowed on this server.");
|
|
13
|
+
}
|
|
14
|
+
const query = safeQuery(sql, ["DELETE"]);
|
|
15
|
+
const result = await runSql(query);
|
|
16
|
+
const parsed = mysqlQueryResultSchema.safeParse(result);
|
|
17
|
+
if (!parsed.success) {
|
|
18
|
+
throw new ToolError(`Invalid query result: ${parsed.error.message}`);
|
|
19
|
+
}
|
|
20
|
+
return parsed.data;
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
//# sourceMappingURL=mysql_delete.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mysql_delete.js","sourceRoot":"","sources":["../../src/tools/mysql_delete.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAE/D,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,MAAM,MAAM,oBAAoB,CAAC;AAExC,MAAM,CAAC,MAAM,YAAY,GAAG,UAAU,CAAC;IACrC,IAAI,EAAE,cAAc;IACpB,WAAW,EACT,6PAA6P;IAC/P,WAAW,EAAE,qBAAqB;IAClC,YAAY,EAAE,qBAAqB;IACnC,OAAO,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;QACzB,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YACxB,MAAM,IAAI,SAAS,CAAC,iDAAiD,CAAC,CAAC;QACzE,CAAC;QACD,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;QACzC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,CAAC;QACnC,MAAM,MAAM,GAAG,sBAAsB,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACxD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,MAAM,IAAI,SAAS,CAAC,yBAAyB,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACvE,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mysql_insert.d.ts","sourceRoot":"","sources":["../../src/tools/mysql_insert.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,YAAY,mDAkBvB,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { defineTool, ToolError } from "@achmadya-dev/mcp-core";
|
|
2
|
+
import { runSql, safeQuery } from "../mysql/mysql.js";
|
|
3
|
+
import { mysqlQueryInputSchema, mysqlQueryOutputShape, mysqlQueryResultSchema, } from "../mysql/schema.js";
|
|
4
|
+
import config from "../mysql/config.js";
|
|
5
|
+
export const mysql_insert = defineTool({
|
|
6
|
+
name: "mysql_insert",
|
|
7
|
+
description: "Insert new data into the database using INSERT or REPLACE. Only a single query is allowed. If the operation is rejected as not allowed, you must respect this safety restriction and do not attempt to bypass it via terminal commands, custom scripts, or external tools.",
|
|
8
|
+
inputSchema: mysqlQueryInputSchema,
|
|
9
|
+
outputSchema: mysqlQueryOutputShape,
|
|
10
|
+
handler: async ({ sql }) => {
|
|
11
|
+
if (!config.allowInsert) {
|
|
12
|
+
throw new ToolError("INSERT operation is not allowed on this server.");
|
|
13
|
+
}
|
|
14
|
+
const query = safeQuery(sql, ["INSERT", "REPLACE"]);
|
|
15
|
+
const result = await runSql(query);
|
|
16
|
+
const parsed = mysqlQueryResultSchema.safeParse(result);
|
|
17
|
+
if (!parsed.success) {
|
|
18
|
+
throw new ToolError(`Invalid query result: ${parsed.error.message}`);
|
|
19
|
+
}
|
|
20
|
+
return parsed.data;
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
//# sourceMappingURL=mysql_insert.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mysql_insert.js","sourceRoot":"","sources":["../../src/tools/mysql_insert.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAE/D,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,MAAM,MAAM,oBAAoB,CAAC;AAExC,MAAM,CAAC,MAAM,YAAY,GAAG,UAAU,CAAC;IACrC,IAAI,EAAE,cAAc;IACpB,WAAW,EACT,4QAA4Q;IAC9Q,WAAW,EAAE,qBAAqB;IAClC,YAAY,EAAE,qBAAqB;IACnC,OAAO,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;QACzB,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YACxB,MAAM,IAAI,SAAS,CAAC,iDAAiD,CAAC,CAAC;QACzE,CAAC;QACD,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC;QACpD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,CAAC;QACnC,MAAM,MAAM,GAAG,sBAAsB,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACxD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,MAAM,IAAI,SAAS,CAAC,yBAAyB,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACvE,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mysql_select.d.ts","sourceRoot":"","sources":["../../src/tools/mysql_select.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,YAAY,mDAevB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { defineTool, ToolError } from "@achmadya-dev/mcp-core";
|
|
2
|
+
import { runSql, safeQuery } from "../mysql/mysql.js";
|
|
3
|
+
import { mysqlQueryInputSchema, mysqlQueryOutputShape, mysqlQueryResultSchema, } from "../mysql/schema.js";
|
|
4
|
+
export const mysql_select = defineTool({
|
|
5
|
+
name: "mysql_select",
|
|
6
|
+
description: "Read data from the database using SELECT, SHOW, DESCRIBE, EXPLAIN, or DESC. Only a single query is allowed.",
|
|
7
|
+
inputSchema: mysqlQueryInputSchema,
|
|
8
|
+
outputSchema: mysqlQueryOutputShape,
|
|
9
|
+
handler: async ({ sql }) => {
|
|
10
|
+
const query = safeQuery(sql, ["SELECT", "SHOW", "DESCRIBE", "EXPLAIN", "DESC"]);
|
|
11
|
+
const result = await runSql(query);
|
|
12
|
+
const parsed = mysqlQueryResultSchema.safeParse(result);
|
|
13
|
+
if (!parsed.success) {
|
|
14
|
+
throw new ToolError(`Invalid query result: ${parsed.error.message}`);
|
|
15
|
+
}
|
|
16
|
+
return parsed.data;
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
//# sourceMappingURL=mysql_select.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mysql_select.js","sourceRoot":"","sources":["../../src/tools/mysql_select.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAE/D,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,oBAAoB,CAAC;AAE5B,MAAM,CAAC,MAAM,YAAY,GAAG,UAAU,CAAC;IACrC,IAAI,EAAE,cAAc;IACpB,WAAW,EACT,6GAA6G;IAC/G,WAAW,EAAE,qBAAqB;IAClC,YAAY,EAAE,qBAAqB;IACnC,OAAO,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;QACzB,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;QAChF,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,CAAC;QACnC,MAAM,MAAM,GAAG,sBAAsB,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACxD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,MAAM,IAAI,SAAS,CAAC,yBAAyB,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACvE,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mysql_update.d.ts","sourceRoot":"","sources":["../../src/tools/mysql_update.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,YAAY,mDAkBvB,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { defineTool, ToolError } from "@achmadya-dev/mcp-core";
|
|
2
|
+
import { runSql, safeQuery } from "../mysql/mysql.js";
|
|
3
|
+
import { mysqlQueryInputSchema, mysqlQueryOutputShape, mysqlQueryResultSchema, } from "../mysql/schema.js";
|
|
4
|
+
import config from "../mysql/config.js";
|
|
5
|
+
export const mysql_update = defineTool({
|
|
6
|
+
name: "mysql_update",
|
|
7
|
+
description: "Update existing data in the database using UPDATE. Only a single query is allowed. If the operation is rejected as not allowed, you must respect this safety restriction and do not attempt to bypass it via terminal commands, custom scripts, or external tools.",
|
|
8
|
+
inputSchema: mysqlQueryInputSchema,
|
|
9
|
+
outputSchema: mysqlQueryOutputShape,
|
|
10
|
+
handler: async ({ sql }) => {
|
|
11
|
+
if (!config.allowUpdate) {
|
|
12
|
+
throw new ToolError("UPDATE operation is not allowed on this server.");
|
|
13
|
+
}
|
|
14
|
+
const query = safeQuery(sql, ["UPDATE"]);
|
|
15
|
+
const result = await runSql(query);
|
|
16
|
+
const parsed = mysqlQueryResultSchema.safeParse(result);
|
|
17
|
+
if (!parsed.success) {
|
|
18
|
+
throw new ToolError(`Invalid query result: ${parsed.error.message}`);
|
|
19
|
+
}
|
|
20
|
+
return parsed.data;
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
//# sourceMappingURL=mysql_update.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mysql_update.js","sourceRoot":"","sources":["../../src/tools/mysql_update.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAE/D,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,MAAM,MAAM,oBAAoB,CAAC;AAExC,MAAM,CAAC,MAAM,YAAY,GAAG,UAAU,CAAC;IACrC,IAAI,EAAE,cAAc;IACpB,WAAW,EACT,oQAAoQ;IACtQ,WAAW,EAAE,qBAAqB;IAClC,YAAY,EAAE,qBAAqB;IACnC,OAAO,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;QACzB,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YACxB,MAAM,IAAI,SAAS,CAAC,iDAAiD,CAAC,CAAC;QACzE,CAAC;QACD,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;QACzC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,CAAC;QACnC,MAAM,MAAM,GAAG,sBAAsB,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACxD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,MAAM,IAAI,SAAS,CAAC,yBAAyB,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACvE,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC;CACF,CAAC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@achmadya-dev/mcp-mysql-query",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Model Context Protocol (MCP) server for MySQL to run SQL queries via stdio (read-only by default)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"author": "achmadya",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/achmadya-dev/mcp-mysql-query.git"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"mcp",
|
|
14
|
+
"model-context-protocol",
|
|
15
|
+
"mysql",
|
|
16
|
+
"database",
|
|
17
|
+
"cursor"
|
|
18
|
+
],
|
|
19
|
+
"main": "dist/index.js",
|
|
20
|
+
"bin": {
|
|
21
|
+
"mcp-mysql-query": "./dist/index.js"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist"
|
|
25
|
+
],
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=20"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@achmadya-dev/mcp-core": "^0.3.3",
|
|
34
|
+
"mysql2": "3.11.0",
|
|
35
|
+
"zod": "4.3.6"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@jest/globals": "30.4.1",
|
|
39
|
+
"@types/jest": "30.0.0",
|
|
40
|
+
"@types/node": "22.10.0",
|
|
41
|
+
"jest": "30.4.2",
|
|
42
|
+
"ts-jest": "29.4.11",
|
|
43
|
+
"typescript": "5.7.2",
|
|
44
|
+
"@changesets/cli": "2.29.8"
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"build": "tsc -p tsconfig.build.json",
|
|
48
|
+
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
|
|
49
|
+
"start": "node dist/index.js",
|
|
50
|
+
"changeset": "changeset",
|
|
51
|
+
"version-packages": "changeset version",
|
|
52
|
+
"publish-packages": "pnpm run build && changeset publish"
|
|
53
|
+
}
|
|
54
|
+
}
|