@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.
Files changed (75) hide show
  1. package/README.md +19 -97
  2. package/dist/adapters/factory.d.ts +0 -4
  3. package/dist/adapters/factory.d.ts.map +1 -1
  4. package/dist/adapters/factory.js +3 -8
  5. package/dist/adapters/factory.js.map +1 -1
  6. package/dist/adapters/mysql/adapter.d.ts +2 -31
  7. package/dist/adapters/mysql/adapter.d.ts.map +1 -1
  8. package/dist/adapters/mysql/adapter.js +27 -283
  9. package/dist/adapters/mysql/adapter.js.map +1 -1
  10. package/dist/adapters/postgresql/adapter.d.ts +2 -22
  11. package/dist/adapters/postgresql/adapter.d.ts.map +1 -1
  12. package/dist/adapters/postgresql/adapter.js +18 -175
  13. package/dist/adapters/postgresql/adapter.js.map +1 -1
  14. package/dist/adapters/sqlite/adapter.d.ts +3 -25
  15. package/dist/adapters/sqlite/adapter.d.ts.map +1 -1
  16. package/dist/adapters/sqlite/adapter.js +65 -364
  17. package/dist/adapters/sqlite/adapter.js.map +1 -1
  18. package/dist/adapters/sqlite/pragma-check.d.ts +19 -0
  19. package/dist/adapters/sqlite/pragma-check.d.ts.map +1 -0
  20. package/dist/adapters/sqlite/pragma-check.js +25 -0
  21. package/dist/adapters/sqlite/pragma-check.js.map +1 -0
  22. package/dist/adapters/sqlite/url-parser.d.ts +34 -0
  23. package/dist/adapters/sqlite/url-parser.d.ts.map +1 -0
  24. package/dist/adapters/sqlite/url-parser.js +73 -0
  25. package/dist/adapters/sqlite/url-parser.js.map +1 -0
  26. package/dist/adapters/types.d.ts +10 -113
  27. package/dist/adapters/types.d.ts.map +1 -1
  28. package/dist/config/loader.d.ts.map +1 -1
  29. package/dist/config/loader.js.map +1 -1
  30. package/dist/server.d.ts.map +1 -1
  31. package/dist/server.js +31 -20
  32. package/dist/server.js.map +1 -1
  33. package/dist/tools/describe-table.d.ts +5 -6
  34. package/dist/tools/describe-table.d.ts.map +1 -1
  35. package/dist/tools/describe-table.js +65 -9
  36. package/dist/tools/describe-table.js.map +1 -1
  37. package/dist/tools/execute.d.ts +5 -6
  38. package/dist/tools/execute.d.ts.map +1 -1
  39. package/dist/tools/execute.js +13 -11
  40. package/dist/tools/execute.js.map +1 -1
  41. package/dist/tools/explain.d.ts +5 -10
  42. package/dist/tools/explain.d.ts.map +1 -1
  43. package/dist/tools/explain.js +25 -26
  44. package/dist/tools/explain.js.map +1 -1
  45. package/dist/tools/list-databases.d.ts +5 -7
  46. package/dist/tools/list-databases.d.ts.map +1 -1
  47. package/dist/tools/list-databases.js +31 -0
  48. package/dist/tools/list-databases.js.map +1 -1
  49. package/dist/tools/list-tables.d.ts +5 -10
  50. package/dist/tools/list-tables.d.ts.map +1 -1
  51. package/dist/tools/list-tables.js +27 -9
  52. package/dist/tools/list-tables.js.map +1 -1
  53. package/dist/tools/query.d.ts +1 -6
  54. package/dist/tools/query.d.ts.map +1 -1
  55. package/dist/tools/query.js +7 -27
  56. package/dist/tools/query.js.map +1 -1
  57. package/dist/types.d.ts +39 -5
  58. package/dist/types.d.ts.map +1 -1
  59. package/dist/utils/formatter.d.ts +7 -4
  60. package/dist/utils/formatter.d.ts.map +1 -1
  61. package/dist/utils/formatter.js +20 -10
  62. package/dist/utils/formatter.js.map +1 -1
  63. package/dist/utils/sql-parser.d.ts +51 -0
  64. package/dist/utils/sql-parser.d.ts.map +1 -0
  65. package/dist/utils/sql-parser.js +310 -0
  66. package/dist/utils/sql-parser.js.map +1 -0
  67. package/dist/utils/truncate.d.ts +4 -13
  68. package/dist/utils/truncate.d.ts.map +1 -1
  69. package/dist/utils/truncate.js +38 -18
  70. package/dist/utils/truncate.js.map +1 -1
  71. package/dist/utils/validation.d.ts +35 -0
  72. package/dist/utils/validation.d.ts.map +1 -0
  73. package/dist/utils/validation.js +89 -0
  74. package/dist/utils/validation.js.map +1 -0
  75. 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.1.0",
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
- "dev": "tsc --watch",
13
- "start": "node dist/index.js",
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
- "test:integration:watch": "vitest --config vitest.config.integration.ts",
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
- "pg": "^8.11.0",
52
- "pgsql-ast-parser": "^12.0.0"
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
  }