@achmadya-dev/mcp-sqlite-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 +108 -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/sqlite/config.d.ts +10 -0
- package/dist/sqlite/config.d.ts.map +1 -0
- package/dist/sqlite/config.js +10 -0
- package/dist/sqlite/config.js.map +1 -0
- package/dist/sqlite/helpers.d.ts +17 -0
- package/dist/sqlite/helpers.d.ts.map +1 -0
- package/dist/sqlite/helpers.js +193 -0
- package/dist/sqlite/helpers.js.map +1 -0
- package/dist/sqlite/schema.d.ts +33 -0
- package/dist/sqlite/schema.d.ts.map +1 -0
- package/dist/sqlite/schema.js +36 -0
- package/dist/sqlite/schema.js.map +1 -0
- package/dist/sqlite/sqlite.d.ts +15 -0
- package/dist/sqlite/sqlite.d.ts.map +1 -0
- package/dist/sqlite/sqlite.js +56 -0
- package/dist/sqlite/sqlite.js.map +1 -0
- package/dist/tools/sqlite_ddl.d.ts +2 -0
- package/dist/tools/sqlite_ddl.d.ts.map +1 -0
- package/dist/tools/sqlite_ddl.js +23 -0
- package/dist/tools/sqlite_ddl.js.map +1 -0
- package/dist/tools/sqlite_delete.d.ts +2 -0
- package/dist/tools/sqlite_delete.d.ts.map +1 -0
- package/dist/tools/sqlite_delete.js +23 -0
- package/dist/tools/sqlite_delete.js.map +1 -0
- package/dist/tools/sqlite_insert.d.ts +2 -0
- package/dist/tools/sqlite_insert.d.ts.map +1 -0
- package/dist/tools/sqlite_insert.js +23 -0
- package/dist/tools/sqlite_insert.js.map +1 -0
- package/dist/tools/sqlite_select.d.ts +2 -0
- package/dist/tools/sqlite_select.d.ts.map +1 -0
- package/dist/tools/sqlite_select.js +19 -0
- package/dist/tools/sqlite_select.js.map +1 -0
- package/dist/tools/sqlite_update.d.ts +2 -0
- package/dist/tools/sqlite_update.d.ts.map +1 -0
- package/dist/tools/sqlite_update.js +23 -0
- package/dist/tools/sqlite_update.js.map +1 -0
- package/package.json +53 -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,108 @@
|
|
|
1
|
+
# @achmadya-dev/mcp-sqlite-query
|
|
2
|
+
|
|
3
|
+
MCP server for SQLite. Runs a single SQL statement per tool call over **stdio** using Node.js built-in `node:sqlite` (no native add-ons). **Read-only by default** — writes and DDL require explicit env flags.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
- Node.js **≥ 22.5.0** (`node:sqlite` built-in)
|
|
8
|
+
- A SQLite database file path, or `:memory:` for ephemeral storage
|
|
9
|
+
|
|
10
|
+
## Install from npm
|
|
11
|
+
|
|
12
|
+
```json
|
|
13
|
+
{
|
|
14
|
+
"mcpServers": {
|
|
15
|
+
"sqlite": {
|
|
16
|
+
"command": "npx",
|
|
17
|
+
"args": ["-y", "@achmadya-dev/mcp-sqlite-query"],
|
|
18
|
+
"env": {
|
|
19
|
+
"SQLITE_DB_PATH": "/absolute/path/to/database.db"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Use `:memory:` for an in-memory database (data is lost when the server stops).
|
|
27
|
+
|
|
28
|
+
Or use `envFile` instead of inline `env`.
|
|
29
|
+
|
|
30
|
+
## Develop from source
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
cp .env.example .env
|
|
34
|
+
pnpm install
|
|
35
|
+
docker compose up -d sqlite
|
|
36
|
+
pnpm --filter @achmadya-dev/mcp-sqlite-query run build
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Docker creates `docker/sqlite/data/dev.db` from `docker/sqlite/init.sql` (sample e-commerce schema).
|
|
40
|
+
|
|
41
|
+
`.cursor/mcp.json`:
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"mcpServers": {
|
|
46
|
+
"sqlite": {
|
|
47
|
+
"command": "node",
|
|
48
|
+
"args": ["${workspaceFolder}/packages/mcp-sqlite-query/dist/index.js"],
|
|
49
|
+
"envFile": "${workspaceFolder}/.env"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Relevant `.env` key:
|
|
56
|
+
|
|
57
|
+
```env
|
|
58
|
+
SQLITE_DB_PATH=docker/sqlite/data/dev.db
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Path is relative to the workspace root when Cursor starts the server.
|
|
62
|
+
|
|
63
|
+
## Environment variables
|
|
64
|
+
|
|
65
|
+
### Database
|
|
66
|
+
|
|
67
|
+
| Variable | Default | Description |
|
|
68
|
+
| ----------------- | ---------- | -------------------------------- |
|
|
69
|
+
| `SQLITE_DB_PATH` | `:memory:` | Database file path or `:memory:` |
|
|
70
|
+
| `SQLITE_MAX_ROWS` | `500` | Max rows for `SELECT` results |
|
|
71
|
+
|
|
72
|
+
### Write access
|
|
73
|
+
|
|
74
|
+
| Variable | Allows |
|
|
75
|
+
| ------------------------ | ----------------------------------------- |
|
|
76
|
+
| `ALLOW_INSERT_OPERATION` | `INSERT`, `REPLACE` |
|
|
77
|
+
| `ALLOW_UPDATE_OPERATION` | `UPDATE` |
|
|
78
|
+
| `ALLOW_DELETE_OPERATION` | `DELETE` |
|
|
79
|
+
| `ALLOW_DDL_OPERATION` | DDL (`CREATE`, `ALTER`, `DROP`, `VACUUM`) |
|
|
80
|
+
|
|
81
|
+
Enabled values: `true`, `1`, `yes`, `on`.
|
|
82
|
+
|
|
83
|
+
## Tools
|
|
84
|
+
|
|
85
|
+
| Tool | Statements | Env flag |
|
|
86
|
+
| --------------- | ----------------------------- | ------------------------ |
|
|
87
|
+
| `sqlite_select` | `SELECT`, `PRAGMA`, `EXPLAIN` | always on |
|
|
88
|
+
| `sqlite_insert` | `INSERT`, `REPLACE` | `ALLOW_INSERT_OPERATION` |
|
|
89
|
+
| `sqlite_update` | `UPDATE` | `ALLOW_UPDATE_OPERATION` |
|
|
90
|
+
| `sqlite_delete` | `DELETE` | `ALLOW_DELETE_OPERATION` |
|
|
91
|
+
| `sqlite_ddl` | DDL | `ALLOW_DDL_OPERATION` |
|
|
92
|
+
|
|
93
|
+
Each tool accepts one `sql` string.
|
|
94
|
+
|
|
95
|
+
## Behavior and security
|
|
96
|
+
|
|
97
|
+
- One SQL statement per request.
|
|
98
|
+
- SQL is validated with a parser that handles string literals, quoted identifiers, and comments before extracting the statement keyword.
|
|
99
|
+
- Always blocked: `XP_CMDSHELL`, `EXEC`, `EXECUTE`, `PREPARE`, `DEALLOCATE`, `ATTACH DATABASE`, `LOAD_FILE`, `INTO OUTFILE`, `COPY ... PROGRAM`.
|
|
100
|
+
- `SELECT` results are capped by `SQLITE_MAX_ROWS`.
|
|
101
|
+
|
|
102
|
+
## Package scripts
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
pnpm run build
|
|
106
|
+
pnpm test
|
|
107
|
+
pnpm start
|
|
108
|
+
```
|
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 { sqlite_ddl } from "./tools/sqlite_ddl.js";
|
|
5
|
+
import { sqlite_delete } from "./tools/sqlite_delete.js";
|
|
6
|
+
import { sqlite_insert } from "./tools/sqlite_insert.js";
|
|
7
|
+
import { sqlite_select } from "./tools/sqlite_select.js";
|
|
8
|
+
import { sqlite_update } from "./tools/sqlite_update.js";
|
|
9
|
+
await startMcpServer({
|
|
10
|
+
name: "SQLite Database",
|
|
11
|
+
version: packageJson.version,
|
|
12
|
+
tools: [sqlite_select, sqlite_insert, sqlite_update, sqlite_delete, sqlite_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,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEzD,MAAM,cAAc,CAAC;IACnB,IAAI,EAAE,iBAAiB;IACvB,OAAO,EAAE,WAAW,CAAC,OAAO;IAC5B,KAAK,EAAE,CAAC,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,UAAU,CAAC;CAChF,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/sqlite/config.ts"],"names":[],"mappings":";;;;;;;;AAEA,wBAOE"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { envBool, envInt, envStr } from "@achmadya-dev/mcp-core";
|
|
2
|
+
export default {
|
|
3
|
+
dbPath: envStr("SQLITE_DB_PATH", ":memory:"),
|
|
4
|
+
maxRows: envInt("SQLITE_MAX_ROWS", 500),
|
|
5
|
+
allowInsert: envBool("ALLOW_INSERT_OPERATION"),
|
|
6
|
+
allowUpdate: envBool("ALLOW_UPDATE_OPERATION"),
|
|
7
|
+
allowDelete: envBool("ALLOW_DELETE_OPERATION"),
|
|
8
|
+
allowDdl: envBool("ALLOW_DDL_OPERATION"),
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/sqlite/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAEjE,eAAe;IACb,MAAM,EAAE,MAAM,CAAC,gBAAgB,EAAE,UAAU,CAAC;IAC5C,OAAO,EAAE,MAAM,CAAC,iBAAiB,EAAE,GAAG,CAAC;IACvC,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/sqlite/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/sqlite/helpers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const sqliteQueryInputSchema: z.ZodObject<{
|
|
3
|
+
sql: z.ZodString;
|
|
4
|
+
}, z.core.$strip>;
|
|
5
|
+
export declare const sqliteQueryOutputShape: 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 sqliteQueryResultSchema: 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 SqliteQueryResult = z.infer<typeof sqliteQueryResultSchema>;
|
|
33
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/sqlite/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,sBAAsB;;iBASjC,CAAC;AAEH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;iBAUjC,CAAC;AAEH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;2BAelC,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,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 sqliteQueryInputSchema = 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 sqliteQueryOutputShape = 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 sqliteQueryResultSchema = 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/sqlite/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,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7C,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,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7C,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,uBAAuB,GAAG,CAAC,CAAC,kBAAkB,CAAC,MAAM,EAAE;IAClE,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,15 @@
|
|
|
1
|
+
export declare function safeQuery(sql: string, allowedPrefixes: string[]): string;
|
|
2
|
+
export declare function runSql(sql: string): Promise<{
|
|
3
|
+
kind: "resultset";
|
|
4
|
+
columns: string[];
|
|
5
|
+
rowCount: number;
|
|
6
|
+
totalRows: number;
|
|
7
|
+
truncated: boolean;
|
|
8
|
+
maxRows: number;
|
|
9
|
+
rows: Record<string, unknown>[];
|
|
10
|
+
} | {
|
|
11
|
+
kind: "execute";
|
|
12
|
+
affectedRows: number;
|
|
13
|
+
insertId: string | null;
|
|
14
|
+
}>;
|
|
15
|
+
//# sourceMappingURL=sqlite.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../src/sqlite/sqlite.ts"],"names":[],"mappings":"AAeA,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,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;CACjC,GACD;IACE,IAAI,EAAE,SAAS,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,CACJ,CA+CA"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { DatabaseSync } from "node:sqlite";
|
|
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 db = null;
|
|
13
|
+
try {
|
|
14
|
+
db = new DatabaseSync(config.dbPath);
|
|
15
|
+
const stmt = db.prepare(sql);
|
|
16
|
+
const columnsInfo = stmt.columns();
|
|
17
|
+
if (columnsInfo && columnsInfo.length > 0) {
|
|
18
|
+
const columns = columnsInfo.map((c) => c.name || "");
|
|
19
|
+
const all = stmt.all();
|
|
20
|
+
const truncated = all.length > config.maxRows;
|
|
21
|
+
const display = all.slice(0, config.maxRows);
|
|
22
|
+
return {
|
|
23
|
+
kind: "resultset",
|
|
24
|
+
columns,
|
|
25
|
+
rowCount: display.length,
|
|
26
|
+
totalRows: all.length,
|
|
27
|
+
truncated,
|
|
28
|
+
maxRows: config.maxRows,
|
|
29
|
+
rows: display,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
const result = stmt.run();
|
|
33
|
+
const insertId = result.lastInsertRowid !== undefined && result.lastInsertRowid !== null
|
|
34
|
+
? String(result.lastInsertRowid)
|
|
35
|
+
: null;
|
|
36
|
+
return {
|
|
37
|
+
kind: "execute",
|
|
38
|
+
affectedRows: Number(result.changes ?? 0),
|
|
39
|
+
insertId,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
catch (e) {
|
|
43
|
+
throw new ToolError(`SQLite: ${e instanceof Error ? e.message : String(e)}`);
|
|
44
|
+
}
|
|
45
|
+
finally {
|
|
46
|
+
if (db) {
|
|
47
|
+
try {
|
|
48
|
+
db.close();
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// Ignore close errors
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=sqlite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite.js","sourceRoot":"","sources":["../../src/sqlite/sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAsB,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AAYxC,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,EAAE,GAAwB,IAAI,CAAC;IACnC,IAAI,CAAC;QACH,EAAE,GAAG,IAAI,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAErC,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAA6B,CAAC;QACzD,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;QAEnC,IAAI,WAAW,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1C,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;YACrD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAA+B,CAAC;YACpD,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;YAE7C,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;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC1B,MAAM,QAAQ,GACZ,MAAM,CAAC,eAAe,KAAK,SAAS,IAAI,MAAM,CAAC,eAAe,KAAK,IAAI;YACrE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC;YAChC,CAAC,CAAC,IAAI,CAAC;QAEX,OAAO;YACL,IAAI,EAAE,SAAS;YACf,YAAY,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,CAAC;YACzC,QAAQ;SACT,CAAC;IACJ,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,IAAI,SAAS,CAAC,WAAW,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC/E,CAAC;YAAS,CAAC;QACT,IAAI,EAAE,EAAE,CAAC;YACP,IAAI,CAAC;gBACH,EAAE,CAAC,KAAK,EAAE,CAAC;YACb,CAAC;YAAC,MAAM,CAAC;gBACP,sBAAsB;YACxB,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite_ddl.d.ts","sourceRoot":"","sources":["../../src/tools/sqlite_ddl.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,UAAU,mDAkBrB,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { defineTool, ToolError } from "@achmadya-dev/mcp-core";
|
|
2
|
+
import { runSql, safeQuery } from "../sqlite/sqlite.js";
|
|
3
|
+
import { sqliteQueryInputSchema, sqliteQueryOutputShape, sqliteQueryResultSchema, } from "../sqlite/schema.js";
|
|
4
|
+
import config from "../sqlite/config.js";
|
|
5
|
+
export const sqlite_ddl = defineTool({
|
|
6
|
+
name: "sqlite_ddl",
|
|
7
|
+
description: "Modify the SQLite database schema using CREATE, ALTER, DROP, or VACUUM. 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: sqliteQueryInputSchema,
|
|
9
|
+
outputSchema: sqliteQueryOutputShape,
|
|
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, ["CREATE", "ALTER", "DROP", "VACUUM"]);
|
|
15
|
+
const result = await runSql(query);
|
|
16
|
+
const parsed = sqliteQueryResultSchema.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=sqlite_ddl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite_ddl.js","sourceRoot":"","sources":["../../src/tools/sqlite_ddl.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAE/D,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EACL,sBAAsB,EACtB,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,MAAM,MAAM,qBAAqB,CAAC;AAEzC,MAAM,CAAC,MAAM,UAAU,GAAG,UAAU,CAAC;IACnC,IAAI,EAAE,YAAY;IAClB,WAAW,EACT,yRAAyR;IAC3R,WAAW,EAAE,sBAAsB;IACnC,YAAY,EAAE,sBAAsB;IACpC,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,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;QACpE,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,CAAC;QACnC,MAAM,MAAM,GAAG,uBAAuB,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACzD,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":"sqlite_delete.d.ts","sourceRoot":"","sources":["../../src/tools/sqlite_delete.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,aAAa,mDAkBxB,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { defineTool, ToolError } from "@achmadya-dev/mcp-core";
|
|
2
|
+
import { runSql, safeQuery } from "../sqlite/sqlite.js";
|
|
3
|
+
import { sqliteQueryInputSchema, sqliteQueryOutputShape, sqliteQueryResultSchema, } from "../sqlite/schema.js";
|
|
4
|
+
import config from "../sqlite/config.js";
|
|
5
|
+
export const sqlite_delete = defineTool({
|
|
6
|
+
name: "sqlite_delete",
|
|
7
|
+
description: "Delete data from the SQLite 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: sqliteQueryInputSchema,
|
|
9
|
+
outputSchema: sqliteQueryOutputShape,
|
|
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 = sqliteQueryResultSchema.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=sqlite_delete.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite_delete.js","sourceRoot":"","sources":["../../src/tools/sqlite_delete.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAE/D,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EACL,sBAAsB,EACtB,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,MAAM,MAAM,qBAAqB,CAAC;AAEzC,MAAM,CAAC,MAAM,aAAa,GAAG,UAAU,CAAC;IACtC,IAAI,EAAE,eAAe;IACrB,WAAW,EACT,oQAAoQ;IACtQ,WAAW,EAAE,sBAAsB;IACnC,YAAY,EAAE,sBAAsB;IACpC,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,uBAAuB,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACzD,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":"sqlite_insert.d.ts","sourceRoot":"","sources":["../../src/tools/sqlite_insert.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,aAAa,mDAkBxB,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { defineTool, ToolError } from "@achmadya-dev/mcp-core";
|
|
2
|
+
import { runSql, safeQuery } from "../sqlite/sqlite.js";
|
|
3
|
+
import { sqliteQueryInputSchema, sqliteQueryOutputShape, sqliteQueryResultSchema, } from "../sqlite/schema.js";
|
|
4
|
+
import config from "../sqlite/config.js";
|
|
5
|
+
export const sqlite_insert = defineTool({
|
|
6
|
+
name: "sqlite_insert",
|
|
7
|
+
description: "Insert new data into the SQLite 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: sqliteQueryInputSchema,
|
|
9
|
+
outputSchema: sqliteQueryOutputShape,
|
|
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 = sqliteQueryResultSchema.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=sqlite_insert.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite_insert.js","sourceRoot":"","sources":["../../src/tools/sqlite_insert.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAE/D,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EACL,sBAAsB,EACtB,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,MAAM,MAAM,qBAAqB,CAAC;AAEzC,MAAM,CAAC,MAAM,aAAa,GAAG,UAAU,CAAC;IACtC,IAAI,EAAE,eAAe;IACrB,WAAW,EACT,mRAAmR;IACrR,WAAW,EAAE,sBAAsB;IACnC,YAAY,EAAE,sBAAsB;IACpC,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,uBAAuB,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACzD,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":"sqlite_select.d.ts","sourceRoot":"","sources":["../../src/tools/sqlite_select.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,aAAa,mDAexB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { defineTool, ToolError } from "@achmadya-dev/mcp-core";
|
|
2
|
+
import { runSql, safeQuery } from "../sqlite/sqlite.js";
|
|
3
|
+
import { sqliteQueryInputSchema, sqliteQueryOutputShape, sqliteQueryResultSchema, } from "../sqlite/schema.js";
|
|
4
|
+
export const sqlite_select = defineTool({
|
|
5
|
+
name: "sqlite_select",
|
|
6
|
+
description: "Read data from the SQLite database using SELECT, PRAGMA, or EXPLAIN. Only a single query is allowed.",
|
|
7
|
+
inputSchema: sqliteQueryInputSchema,
|
|
8
|
+
outputSchema: sqliteQueryOutputShape,
|
|
9
|
+
handler: async ({ sql }) => {
|
|
10
|
+
const query = safeQuery(sql, ["SELECT", "PRAGMA", "EXPLAIN"]);
|
|
11
|
+
const result = await runSql(query);
|
|
12
|
+
const parsed = sqliteQueryResultSchema.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=sqlite_select.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite_select.js","sourceRoot":"","sources":["../../src/tools/sqlite_select.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAE/D,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EACL,sBAAsB,EACtB,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,qBAAqB,CAAC;AAE7B,MAAM,CAAC,MAAM,aAAa,GAAG,UAAU,CAAC;IACtC,IAAI,EAAE,eAAe;IACrB,WAAW,EACT,sGAAsG;IACxG,WAAW,EAAE,sBAAsB;IACnC,YAAY,EAAE,sBAAsB;IACpC,OAAO,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;QACzB,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC;QAC9D,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,CAAC;QACnC,MAAM,MAAM,GAAG,uBAAuB,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACzD,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":"sqlite_update.d.ts","sourceRoot":"","sources":["../../src/tools/sqlite_update.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,aAAa,mDAkBxB,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { defineTool, ToolError } from "@achmadya-dev/mcp-core";
|
|
2
|
+
import { runSql, safeQuery } from "../sqlite/sqlite.js";
|
|
3
|
+
import { sqliteQueryInputSchema, sqliteQueryOutputShape, sqliteQueryResultSchema, } from "../sqlite/schema.js";
|
|
4
|
+
import config from "../sqlite/config.js";
|
|
5
|
+
export const sqlite_update = defineTool({
|
|
6
|
+
name: "sqlite_update",
|
|
7
|
+
description: "Update existing data in the SQLite 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: sqliteQueryInputSchema,
|
|
9
|
+
outputSchema: sqliteQueryOutputShape,
|
|
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 = sqliteQueryResultSchema.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=sqlite_update.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite_update.js","sourceRoot":"","sources":["../../src/tools/sqlite_update.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAE/D,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EACL,sBAAsB,EACtB,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,MAAM,MAAM,qBAAqB,CAAC;AAEzC,MAAM,CAAC,MAAM,aAAa,GAAG,UAAU,CAAC;IACtC,IAAI,EAAE,eAAe;IACrB,WAAW,EACT,2QAA2Q;IAC7Q,WAAW,EAAE,sBAAsB;IACnC,YAAY,EAAE,sBAAsB;IACpC,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,uBAAuB,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACzD,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,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@achmadya-dev/mcp-sqlite-query",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Model Context Protocol (MCP) server for SQLite 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-sqlite-query.git"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"mcp",
|
|
14
|
+
"model-context-protocol",
|
|
15
|
+
"sqlite",
|
|
16
|
+
"database",
|
|
17
|
+
"cursor"
|
|
18
|
+
],
|
|
19
|
+
"main": "dist/index.js",
|
|
20
|
+
"bin": {
|
|
21
|
+
"mcp-sqlite-query": "./dist/index.js"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist"
|
|
25
|
+
],
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=22.5.0"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@achmadya-dev/mcp-core": "^0.3.3",
|
|
34
|
+
"zod": "4.3.6"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@jest/globals": "30.4.1",
|
|
38
|
+
"@types/jest": "30.0.0",
|
|
39
|
+
"@types/node": "22.10.0",
|
|
40
|
+
"jest": "30.4.2",
|
|
41
|
+
"ts-jest": "29.4.11",
|
|
42
|
+
"typescript": "5.7.2",
|
|
43
|
+
"@changesets/cli": "2.29.8"
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"build": "tsc -p tsconfig.build.json",
|
|
47
|
+
"test": "node --experimental-sqlite --experimental-vm-modules node_modules/jest/bin/jest.js",
|
|
48
|
+
"start": "node --experimental-sqlite dist/index.js",
|
|
49
|
+
"changeset": "changeset",
|
|
50
|
+
"version-packages": "changeset version",
|
|
51
|
+
"publish-packages": "pnpm run build && changeset publish"
|
|
52
|
+
}
|
|
53
|
+
}
|