@tejasanik/postgres-mcp-server 1.0.0 → 1.1.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 (63) hide show
  1. package/README.md +63 -10
  2. package/dist/__tests__/analysis-tools.test.d.ts +2 -0
  3. package/dist/__tests__/analysis-tools.test.d.ts.map +1 -0
  4. package/dist/__tests__/analysis-tools.test.js +294 -0
  5. package/dist/__tests__/analysis-tools.test.js.map +1 -0
  6. package/dist/__tests__/db-manager.test.d.ts +2 -0
  7. package/dist/__tests__/db-manager.test.d.ts.map +1 -0
  8. package/dist/__tests__/db-manager.test.js +243 -0
  9. package/dist/__tests__/db-manager.test.js.map +1 -0
  10. package/dist/__tests__/mcp-server.test.d.ts +13 -0
  11. package/dist/__tests__/mcp-server.test.d.ts.map +1 -0
  12. package/dist/__tests__/mcp-server.test.js +131 -0
  13. package/dist/__tests__/mcp-server.test.js.map +1 -0
  14. package/dist/__tests__/schema-tools.test.d.ts +2 -0
  15. package/dist/__tests__/schema-tools.test.d.ts.map +1 -0
  16. package/dist/__tests__/schema-tools.test.js +171 -0
  17. package/dist/__tests__/schema-tools.test.js.map +1 -0
  18. package/dist/__tests__/server-tools.test.d.ts +2 -0
  19. package/dist/__tests__/server-tools.test.d.ts.map +1 -0
  20. package/dist/__tests__/server-tools.test.js +94 -0
  21. package/dist/__tests__/server-tools.test.js.map +1 -0
  22. package/dist/__tests__/sql-tools.test.d.ts +2 -0
  23. package/dist/__tests__/sql-tools.test.d.ts.map +1 -0
  24. package/dist/__tests__/sql-tools.test.js +235 -0
  25. package/dist/__tests__/sql-tools.test.js.map +1 -0
  26. package/dist/__tests__/validation.test.d.ts +2 -0
  27. package/dist/__tests__/validation.test.d.ts.map +1 -0
  28. package/dist/__tests__/validation.test.js +203 -0
  29. package/dist/__tests__/validation.test.js.map +1 -0
  30. package/dist/db-manager.d.ts +17 -4
  31. package/dist/db-manager.d.ts.map +1 -1
  32. package/dist/db-manager.js +143 -26
  33. package/dist/db-manager.js.map +1 -1
  34. package/dist/index.js +62 -26
  35. package/dist/index.js.map +1 -1
  36. package/dist/tools/analysis-tools.d.ts +1 -0
  37. package/dist/tools/analysis-tools.d.ts.map +1 -1
  38. package/dist/tools/analysis-tools.js +158 -81
  39. package/dist/tools/analysis-tools.js.map +1 -1
  40. package/dist/tools/index.js +4 -20
  41. package/dist/tools/index.js.map +1 -1
  42. package/dist/tools/schema-tools.d.ts.map +1 -1
  43. package/dist/tools/schema-tools.js +71 -40
  44. package/dist/tools/schema-tools.js.map +1 -1
  45. package/dist/tools/server-tools.d.ts +11 -1
  46. package/dist/tools/server-tools.d.ts.map +1 -1
  47. package/dist/tools/server-tools.js +23 -14
  48. package/dist/tools/server-tools.js.map +1 -1
  49. package/dist/tools/sql-tools.d.ts.map +1 -1
  50. package/dist/tools/sql-tools.js +88 -61
  51. package/dist/tools/sql-tools.js.map +1 -1
  52. package/dist/types.d.ts +13 -0
  53. package/dist/types.d.ts.map +1 -1
  54. package/dist/types.js +1 -2
  55. package/dist/utils/index.d.ts +2 -0
  56. package/dist/utils/index.d.ts.map +1 -0
  57. package/dist/utils/index.js +2 -0
  58. package/dist/utils/index.js.map +1 -0
  59. package/dist/utils/validation.d.ts +27 -0
  60. package/dist/utils/validation.d.ts.map +1 -0
  61. package/dist/utils/validation.js +133 -0
  62. package/dist/utils/validation.js.map +1 -0
  63. package/package.json +8 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Validates and sanitizes PostgreSQL identifiers (table names, column names, etc.)
3
+ * Prevents SQL injection by only allowing valid identifier characters.
4
+ */
5
+ export declare function validateIdentifier(identifier: string, fieldName: string): string;
6
+ /**
7
+ * Escapes a PostgreSQL identifier by doubling any internal double quotes
8
+ * and wrapping in double quotes.
9
+ */
10
+ export declare function escapeIdentifier(identifier: string): string;
11
+ /**
12
+ * Validates SQL for read-only operations more thoroughly.
13
+ * Returns true if the SQL appears to be read-only, false otherwise.
14
+ */
15
+ export declare function isReadOnlySql(sql: string): {
16
+ isReadOnly: boolean;
17
+ reason?: string;
18
+ };
19
+ /**
20
+ * Validates that a number is within acceptable bounds.
21
+ */
22
+ export declare function validatePositiveInteger(value: any, fieldName: string, min?: number, max?: number): number;
23
+ /**
24
+ * Validates index type is one of the allowed PostgreSQL index types.
25
+ */
26
+ export declare function validateIndexType(indexType: string): string;
27
+ //# sourceMappingURL=validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAqBhF;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAQ3D;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,UAAU,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CA4EnF;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,GAAE,MAAU,EAAE,GAAG,GAAE,MAAc,GAAG,MAAM,CAWnH;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAS3D"}
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Validates and sanitizes PostgreSQL identifiers (table names, column names, etc.)
3
+ * Prevents SQL injection by only allowing valid identifier characters.
4
+ */
5
+ export function validateIdentifier(identifier, fieldName) {
6
+ if (!identifier || typeof identifier !== 'string') {
7
+ throw new Error(`${fieldName} is required and must be a string`);
8
+ }
9
+ // PostgreSQL identifier rules:
10
+ // - Must start with a letter (a-z) or underscore
11
+ // - Can contain letters, digits, underscores, and dollar signs
12
+ // - Maximum 63 characters
13
+ // We're being more restrictive for security
14
+ const validIdentifierRegex = /^[a-zA-Z_][a-zA-Z0-9_$]*$/;
15
+ if (identifier.length > 63) {
16
+ throw new Error(`${fieldName} must be 63 characters or less`);
17
+ }
18
+ if (!validIdentifierRegex.test(identifier)) {
19
+ throw new Error(`${fieldName} contains invalid characters. Only letters, numbers, underscores, and dollar signs are allowed, and it must start with a letter or underscore.`);
20
+ }
21
+ return identifier;
22
+ }
23
+ /**
24
+ * Escapes a PostgreSQL identifier by doubling any internal double quotes
25
+ * and wrapping in double quotes.
26
+ */
27
+ export function escapeIdentifier(identifier) {
28
+ // Validate first
29
+ if (!identifier || typeof identifier !== 'string') {
30
+ throw new Error('Identifier is required and must be a string');
31
+ }
32
+ // Double any internal double quotes and wrap in quotes
33
+ return `"${identifier.replace(/"/g, '""')}"`;
34
+ }
35
+ /**
36
+ * Validates SQL for read-only operations more thoroughly.
37
+ * Returns true if the SQL appears to be read-only, false otherwise.
38
+ */
39
+ export function isReadOnlySql(sql) {
40
+ if (!sql || typeof sql !== 'string') {
41
+ return { isReadOnly: false, reason: 'SQL is required' };
42
+ }
43
+ // Normalize: remove comments, extra whitespace
44
+ let normalizedSql = sql
45
+ // Remove single-line comments
46
+ .replace(/--[^\n]*/g, '')
47
+ // Remove multi-line comments
48
+ .replace(/\/\*[\s\S]*?\*\//g, '')
49
+ // Normalize whitespace
50
+ .replace(/\s+/g, ' ')
51
+ .trim()
52
+ .toUpperCase();
53
+ // Check for write operations at any position (not just start)
54
+ const writeOperations = [
55
+ 'INSERT',
56
+ 'UPDATE',
57
+ 'DELETE',
58
+ 'DROP',
59
+ 'CREATE',
60
+ 'ALTER',
61
+ 'TRUNCATE',
62
+ 'GRANT',
63
+ 'REVOKE',
64
+ 'COPY',
65
+ 'VACUUM',
66
+ 'REINDEX',
67
+ 'CLUSTER',
68
+ 'REFRESH MATERIALIZED VIEW',
69
+ 'LOCK',
70
+ 'DISCARD',
71
+ 'RESET',
72
+ 'SET ', // Note: space to avoid matching in column names
73
+ 'DO ', // Anonymous code blocks
74
+ 'CALL', // Stored procedures
75
+ ];
76
+ // Check if any write operation appears as a keyword (word boundary check)
77
+ for (const op of writeOperations) {
78
+ // Check at start or after common statement separators
79
+ const patterns = [
80
+ new RegExp(`^${op}\\b`), // At start
81
+ new RegExp(`\\(\\s*${op}\\b`), // After opening paren (subquery)
82
+ new RegExp(`;\\s*${op}\\b`), // After semicolon
83
+ new RegExp(`\\bWITH\\b.*\\bAS\\s*\\(\\s*${op}\\b`, 's'), // In CTE
84
+ ];
85
+ for (const pattern of patterns) {
86
+ if (pattern.test(normalizedSql)) {
87
+ return { isReadOnly: false, reason: `Write operation '${op.trim()}' detected` };
88
+ }
89
+ }
90
+ }
91
+ // Check for function calls that could have side effects
92
+ const dangerousFunctions = [
93
+ 'LO_IMPORT',
94
+ 'LO_EXPORT',
95
+ 'LO_UNLINK',
96
+ 'PG_READ_FILE',
97
+ 'PG_READ_BINARY_FILE',
98
+ 'PG_WRITE_FILE',
99
+ 'DBLINK_EXEC',
100
+ 'DBLINK',
101
+ ];
102
+ for (const func of dangerousFunctions) {
103
+ if (new RegExp(`\\b${func}\\s*\\(`).test(normalizedSql)) {
104
+ return { isReadOnly: false, reason: `Dangerous function '${func}' detected` };
105
+ }
106
+ }
107
+ return { isReadOnly: true };
108
+ }
109
+ /**
110
+ * Validates that a number is within acceptable bounds.
111
+ */
112
+ export function validatePositiveInteger(value, fieldName, min = 1, max = 10000) {
113
+ if (value === undefined || value === null) {
114
+ return min;
115
+ }
116
+ const num = parseInt(value, 10);
117
+ if (isNaN(num) || num < min || num > max) {
118
+ throw new Error(`${fieldName} must be an integer between ${min} and ${max}`);
119
+ }
120
+ return num;
121
+ }
122
+ /**
123
+ * Validates index type is one of the allowed PostgreSQL index types.
124
+ */
125
+ export function validateIndexType(indexType) {
126
+ const validTypes = ['btree', 'hash', 'gist', 'spgist', 'gin', 'brin'];
127
+ const normalized = (indexType || 'btree').toLowerCase();
128
+ if (!validTypes.includes(normalized)) {
129
+ throw new Error(`Invalid index type '${indexType}'. Must be one of: ${validTypes.join(', ')}`);
130
+ }
131
+ return normalized;
132
+ }
133
+ //# 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,7 +1,8 @@
1
1
  {
2
2
  "name": "@tejasanik/postgres-mcp-server",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "A Model Context Protocol (MCP) server for PostgreSQL database management and analysis",
5
+ "type": "module",
5
6
  "main": "dist/index.js",
6
7
  "types": "dist/index.d.ts",
7
8
  "bin": {
@@ -11,8 +12,10 @@
11
12
  "build": "tsc",
12
13
  "start": "node dist/index.js",
13
14
  "dev": "ts-node src/index.ts",
15
+ "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
16
+ "test:watch": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watch",
14
17
  "prepare": "npm run build",
15
- "prepublishOnly": "npm run build"
18
+ "prepublishOnly": "npm run build && npm test"
16
19
  },
17
20
  "keywords": [
18
21
  "mcp",
@@ -31,9 +34,12 @@
31
34
  "uuid": "^11.0.3"
32
35
  },
33
36
  "devDependencies": {
37
+ "@types/jest": "^29.5.14",
34
38
  "@types/node": "^22.10.2",
35
39
  "@types/pg": "^8.11.10",
36
40
  "@types/uuid": "^10.0.0",
41
+ "jest": "^29.7.0",
42
+ "ts-jest": "^29.2.5",
37
43
  "typescript": "^5.7.2"
38
44
  },
39
45
  "engines": {