@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.
- package/README.md +63 -10
- package/dist/__tests__/analysis-tools.test.d.ts +2 -0
- package/dist/__tests__/analysis-tools.test.d.ts.map +1 -0
- package/dist/__tests__/analysis-tools.test.js +294 -0
- package/dist/__tests__/analysis-tools.test.js.map +1 -0
- package/dist/__tests__/db-manager.test.d.ts +2 -0
- package/dist/__tests__/db-manager.test.d.ts.map +1 -0
- package/dist/__tests__/db-manager.test.js +243 -0
- package/dist/__tests__/db-manager.test.js.map +1 -0
- package/dist/__tests__/mcp-server.test.d.ts +13 -0
- package/dist/__tests__/mcp-server.test.d.ts.map +1 -0
- package/dist/__tests__/mcp-server.test.js +131 -0
- package/dist/__tests__/mcp-server.test.js.map +1 -0
- package/dist/__tests__/schema-tools.test.d.ts +2 -0
- package/dist/__tests__/schema-tools.test.d.ts.map +1 -0
- package/dist/__tests__/schema-tools.test.js +171 -0
- package/dist/__tests__/schema-tools.test.js.map +1 -0
- package/dist/__tests__/server-tools.test.d.ts +2 -0
- package/dist/__tests__/server-tools.test.d.ts.map +1 -0
- package/dist/__tests__/server-tools.test.js +94 -0
- package/dist/__tests__/server-tools.test.js.map +1 -0
- package/dist/__tests__/sql-tools.test.d.ts +2 -0
- package/dist/__tests__/sql-tools.test.d.ts.map +1 -0
- package/dist/__tests__/sql-tools.test.js +235 -0
- package/dist/__tests__/sql-tools.test.js.map +1 -0
- package/dist/__tests__/validation.test.d.ts +2 -0
- package/dist/__tests__/validation.test.d.ts.map +1 -0
- package/dist/__tests__/validation.test.js +203 -0
- package/dist/__tests__/validation.test.js.map +1 -0
- package/dist/db-manager.d.ts +17 -4
- package/dist/db-manager.d.ts.map +1 -1
- package/dist/db-manager.js +143 -26
- package/dist/db-manager.js.map +1 -1
- package/dist/index.js +62 -26
- package/dist/index.js.map +1 -1
- package/dist/tools/analysis-tools.d.ts +1 -0
- package/dist/tools/analysis-tools.d.ts.map +1 -1
- package/dist/tools/analysis-tools.js +158 -81
- package/dist/tools/analysis-tools.js.map +1 -1
- package/dist/tools/index.js +4 -20
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/schema-tools.d.ts.map +1 -1
- package/dist/tools/schema-tools.js +71 -40
- package/dist/tools/schema-tools.js.map +1 -1
- package/dist/tools/server-tools.d.ts +11 -1
- package/dist/tools/server-tools.d.ts.map +1 -1
- package/dist/tools/server-tools.js +23 -14
- package/dist/tools/server-tools.js.map +1 -1
- package/dist/tools/sql-tools.d.ts.map +1 -1
- package/dist/tools/sql-tools.js +88 -61
- package/dist/tools/sql-tools.js.map +1 -1
- package/dist/types.d.ts +13 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -2
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/validation.d.ts +27 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +133 -0
- package/dist/utils/validation.js.map +1 -0
- 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.
|
|
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": {
|