@pilat/mcp-datalink 1.1.0 → 1.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/README.md +19 -97
- package/dist/adapters/factory.d.ts +0 -4
- package/dist/adapters/factory.d.ts.map +1 -1
- package/dist/adapters/factory.js +3 -8
- package/dist/adapters/factory.js.map +1 -1
- package/dist/adapters/mysql/adapter.d.ts +2 -31
- package/dist/adapters/mysql/adapter.d.ts.map +1 -1
- package/dist/adapters/mysql/adapter.js +27 -283
- package/dist/adapters/mysql/adapter.js.map +1 -1
- package/dist/adapters/postgresql/adapter.d.ts +2 -22
- package/dist/adapters/postgresql/adapter.d.ts.map +1 -1
- package/dist/adapters/postgresql/adapter.js +18 -175
- package/dist/adapters/postgresql/adapter.js.map +1 -1
- package/dist/adapters/sqlite/adapter.d.ts +3 -25
- package/dist/adapters/sqlite/adapter.d.ts.map +1 -1
- package/dist/adapters/sqlite/adapter.js +65 -364
- package/dist/adapters/sqlite/adapter.js.map +1 -1
- package/dist/adapters/sqlite/pragma-check.d.ts +19 -0
- package/dist/adapters/sqlite/pragma-check.d.ts.map +1 -0
- package/dist/adapters/sqlite/pragma-check.js +25 -0
- package/dist/adapters/sqlite/pragma-check.js.map +1 -0
- package/dist/adapters/sqlite/url-parser.d.ts +34 -0
- package/dist/adapters/sqlite/url-parser.d.ts.map +1 -0
- package/dist/adapters/sqlite/url-parser.js +73 -0
- package/dist/adapters/sqlite/url-parser.js.map +1 -0
- package/dist/adapters/types.d.ts +10 -113
- package/dist/adapters/types.d.ts.map +1 -1
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +31 -20
- package/dist/server.js.map +1 -1
- package/dist/tools/describe-table.d.ts +5 -6
- package/dist/tools/describe-table.d.ts.map +1 -1
- package/dist/tools/describe-table.js +65 -9
- package/dist/tools/describe-table.js.map +1 -1
- package/dist/tools/execute.d.ts +5 -6
- package/dist/tools/execute.d.ts.map +1 -1
- package/dist/tools/execute.js +13 -11
- package/dist/tools/execute.js.map +1 -1
- package/dist/tools/explain.d.ts +5 -10
- package/dist/tools/explain.d.ts.map +1 -1
- package/dist/tools/explain.js +25 -26
- package/dist/tools/explain.js.map +1 -1
- package/dist/tools/list-databases.d.ts +5 -7
- package/dist/tools/list-databases.d.ts.map +1 -1
- package/dist/tools/list-databases.js +31 -0
- package/dist/tools/list-databases.js.map +1 -1
- package/dist/tools/list-tables.d.ts +5 -10
- package/dist/tools/list-tables.d.ts.map +1 -1
- package/dist/tools/list-tables.js +27 -9
- package/dist/tools/list-tables.js.map +1 -1
- package/dist/tools/query.d.ts +1 -6
- package/dist/tools/query.d.ts.map +1 -1
- package/dist/tools/query.js +7 -27
- package/dist/tools/query.js.map +1 -1
- package/dist/types.d.ts +39 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/formatter.d.ts +7 -4
- package/dist/utils/formatter.d.ts.map +1 -1
- package/dist/utils/formatter.js +20 -10
- package/dist/utils/formatter.js.map +1 -1
- package/dist/utils/sql-parser.d.ts +51 -0
- package/dist/utils/sql-parser.d.ts.map +1 -0
- package/dist/utils/sql-parser.js +310 -0
- package/dist/utils/sql-parser.js.map +1 -0
- package/dist/utils/truncate.d.ts +4 -13
- package/dist/utils/truncate.d.ts.map +1 -1
- package/dist/utils/truncate.js +38 -18
- package/dist/utils/truncate.js.map +1 -1
- package/dist/utils/validation.d.ts +35 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +89 -0
- package/dist/utils/validation.js.map +1 -0
- package/package.json +10 -11
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation utilities for database operations
|
|
3
|
+
*/
|
|
4
|
+
import { DbMcpError, ErrorCode } from './errors.js';
|
|
5
|
+
/**
|
|
6
|
+
* Get a validated database configuration, throwing if not found
|
|
7
|
+
*
|
|
8
|
+
* @param database - The database name to look up
|
|
9
|
+
* @param config - The application configuration
|
|
10
|
+
* @returns The database configuration
|
|
11
|
+
* @throws DbMcpError with DATABASE_NOT_FOUND if database not configured
|
|
12
|
+
*/
|
|
13
|
+
export function getValidatedDatabase(database, config) {
|
|
14
|
+
const dbConfig = config.databases[database];
|
|
15
|
+
if (!dbConfig) {
|
|
16
|
+
throw new DbMcpError(ErrorCode.DATABASE_NOT_FOUND, `Database "${database}" not found in configuration`, { database, available: Object.keys(config.databases) });
|
|
17
|
+
}
|
|
18
|
+
return dbConfig;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Count placeholder parameters in SQL query
|
|
22
|
+
*
|
|
23
|
+
* Counts PostgreSQL-style placeholders ($1, $2, etc.) in SQL.
|
|
24
|
+
* Ignores placeholders inside string literals (single or double quoted).
|
|
25
|
+
*
|
|
26
|
+
* Note: LLM always uses $N style placeholders. For MySQL/SQLite,
|
|
27
|
+
* adapters convert $N to ? via convertPlaceholders() after validation.
|
|
28
|
+
*
|
|
29
|
+
* @param sql - SQL query to analyze
|
|
30
|
+
* @returns Number of unique placeholders found
|
|
31
|
+
*/
|
|
32
|
+
export function countPlaceholders(sql) {
|
|
33
|
+
const placeholders = new Set();
|
|
34
|
+
let inString = false;
|
|
35
|
+
let stringChar = '';
|
|
36
|
+
let i = 0;
|
|
37
|
+
while (i < sql.length) {
|
|
38
|
+
// Handle string literal start
|
|
39
|
+
if (!inString && (sql[i] === "'" || sql[i] === '"')) {
|
|
40
|
+
inString = true;
|
|
41
|
+
stringChar = sql[i];
|
|
42
|
+
i++;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
// Handle string literal end (with escaped quote support)
|
|
46
|
+
if (inString && sql[i] === stringChar) {
|
|
47
|
+
if (i + 1 < sql.length && sql[i + 1] === stringChar) {
|
|
48
|
+
// Escaped quote, skip both characters
|
|
49
|
+
i += 2;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
inString = false;
|
|
54
|
+
i++;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Look for $N placeholders outside strings
|
|
59
|
+
if (!inString && sql[i] === '$') {
|
|
60
|
+
let j = i + 1;
|
|
61
|
+
while (j < sql.length && /\d/.test(sql[j])) {
|
|
62
|
+
j++;
|
|
63
|
+
}
|
|
64
|
+
if (j > i + 1) {
|
|
65
|
+
const num = parseInt(sql.slice(i + 1, j), 10);
|
|
66
|
+
placeholders.add(num);
|
|
67
|
+
i = j;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
i++;
|
|
72
|
+
}
|
|
73
|
+
return placeholders.size;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Validate that the number of parameters matches the placeholder count in SQL
|
|
77
|
+
*
|
|
78
|
+
* @param sql - SQL query with $1, $2, etc. placeholders
|
|
79
|
+
* @param params - Array of parameter values
|
|
80
|
+
* @throws DbMcpError with INVALID_SQL if counts don't match
|
|
81
|
+
*/
|
|
82
|
+
export function validateParamCount(sql, params) {
|
|
83
|
+
const placeholderCount = countPlaceholders(sql);
|
|
84
|
+
const paramCount = params.length;
|
|
85
|
+
if (placeholderCount !== paramCount) {
|
|
86
|
+
throw new DbMcpError(ErrorCode.INVALID_SQL, `Query has ${placeholderCount} placeholder${placeholderCount !== 1 ? 's' : ''} but ${paramCount} parameter${paramCount !== 1 ? 's' : ''} provided`, { sql, placeholderCount, paramCount });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pilat/mcp-datalink",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "MCP server for secure database access (PostgreSQL, MySQL, SQLite)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -9,15 +9,10 @@
|
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"build": "tsc",
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"test": "vitest",
|
|
15
|
-
"test:unit": "vitest --run --exclude 'src/__integration__/**'",
|
|
12
|
+
"test": "vitest --run --exclude 'src/__integration__/**'",
|
|
13
|
+
"test:coverage": "vitest --coverage --run --exclude 'src/__integration__/**'",
|
|
16
14
|
"test:integration": "vitest run --config vitest.config.integration.ts",
|
|
17
|
-
"
|
|
18
|
-
"test:coverage": "vitest --coverage",
|
|
19
|
-
"lint": "eslint src",
|
|
20
|
-
"typecheck": "tsc --noEmit"
|
|
15
|
+
"lint": "eslint src"
|
|
21
16
|
},
|
|
22
17
|
"keywords": [
|
|
23
18
|
"mcp",
|
|
@@ -48,17 +43,21 @@
|
|
|
48
43
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
49
44
|
"better-sqlite3": "^11.7.0",
|
|
50
45
|
"mysql2": "^3.6.0",
|
|
51
|
-
"
|
|
52
|
-
"
|
|
46
|
+
"node-sql-parser": "^5.3.13",
|
|
47
|
+
"pg": "^8.11.0"
|
|
53
48
|
},
|
|
54
49
|
"devDependencies": {
|
|
50
|
+
"@eslint/js": "^9.39.2",
|
|
55
51
|
"@testcontainers/mysql": "^11.11.0",
|
|
56
52
|
"@testcontainers/postgresql": "^11.11.0",
|
|
57
53
|
"@types/better-sqlite3": "^7.6.0",
|
|
58
54
|
"@types/node": "^20.0.0",
|
|
59
55
|
"@types/pg": "^8.11.0",
|
|
56
|
+
"@vitest/coverage-v8": "^1.6.1",
|
|
57
|
+
"eslint": "^9.39.2",
|
|
60
58
|
"testcontainers": "^11.11.0",
|
|
61
59
|
"typescript": "^5.3.0",
|
|
60
|
+
"typescript-eslint": "^8.51.0",
|
|
62
61
|
"vitest": "^1.0.0"
|
|
63
62
|
}
|
|
64
63
|
}
|