postgres-mcp-readonly 1.0.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mohaimanul islam
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,562 @@
1
+ # PostgreSQL MCP Server
2
+
3
+ A secure, read-only PostgreSQL Model Context Protocol (MCP) server that provides safe database introspection and querying capabilities. Built with TypeScript for enhanced type safety and reliability.
4
+
5
+ ## Overview
6
+
7
+ This MCP server enables AI assistants and other MCP clients to safely interact with PostgreSQL databases through a read-only interface. It provides schema inspection, parameterized queries, table previews, change tracking, and row counting while preventing any data modifications.
8
+
9
+ ## Features
10
+
11
+ ### 🔒 Security First
12
+
13
+ - **Read-only enforcement** - Blocks all write operations (INSERT, UPDATE, DELETE, etc.)
14
+ - **SQL injection protection** - Validates identifiers and sanitizes queries
15
+ - **Automatic LIMIT enforcement** - Prevents unbounded result sets
16
+ - **Query timeouts** - Prevents long-running queries from blocking resources
17
+ - **Error sanitization** - Prevents leakage of sensitive connection details
18
+ - **Transaction isolation** - All queries run in READ ONLY transactions
19
+
20
+ ### 🛠️ Tools Provided
21
+
22
+ 1. **db.schema** - Inspect database structure
23
+ 2. **db.query** - Execute parameterized SELECT queries
24
+ 3. **db.preview** - Quick table preview
25
+ 4. **db.watch** - Poll for incremental changes
26
+ 5. **db.count** - Get exact row counts
27
+
28
+ ### 📊 Resources
29
+
30
+ - **schema-summary** (`pg://schema/summary`) - Table list with approximate row counts
31
+ - **schema-full** (`pg://schema/full`) - Complete schema with columns, keys, and relationships
32
+
33
+ ## Installation
34
+
35
+ ### Prerequisites
36
+
37
+ - Node.js 18+
38
+ - PostgreSQL database (accessible via network)
39
+
40
+ ### Setup
41
+
42
+ 1. **Clone or download this project**
43
+
44
+ ```bash
45
+ cd pg-mcp-server
46
+ ```
47
+
48
+ 2. **Install dependencies**
49
+
50
+ ```bash
51
+ npm install
52
+ ```
53
+
54
+ 3. **Build the TypeScript code**
55
+
56
+ ```bash
57
+ npm run build
58
+ ```
59
+
60
+ 4. **Configure environment variables**
61
+
62
+ Create a `.env` file:
63
+
64
+ ```env
65
+ DATABASE_URL=postgres://username:password@localhost:5432/database_name
66
+ STATEMENT_TIMEOUT_MS=5000
67
+ MAX_ROWS=500
68
+ ```
69
+
70
+ 5. **Test the connection**
71
+ ```bash
72
+ npm start
73
+ ```
74
+
75
+ ## Configuration
76
+
77
+ ### Environment Variables
78
+
79
+ | Variable | Required | Default | Description |
80
+ | ---------------------- | -------- | ------- | ----------------------------- |
81
+ | `DATABASE_URL` | ✓ | - | PostgreSQL connection string |
82
+ | `STATEMENT_TIMEOUT_MS` | ✗ | 5000 | Query timeout in milliseconds |
83
+ | `MAX_ROWS` | ✗ | 500 | Default maximum rows returned |
84
+
85
+ ### Connection String Format
86
+
87
+ ```
88
+ postgres://username:password@host:5432/database_name
89
+ postgresql://username:password@host:5432/database_name
90
+ ```
91
+
92
+ ## Tools Documentation
93
+
94
+ ### 1. db.schema
95
+
96
+ Inspect database schema information.
97
+
98
+ **Parameters:**
99
+
100
+ - `mode` (optional): `"summary"` or `"full"` (default: `"summary"`)
101
+ - `filter` (optional): Filter tables by name or schema (case-insensitive)
102
+
103
+ **Examples:**
104
+
105
+ ```javascript
106
+ // Get table list with row counts
107
+ {
108
+ "mode": "summary"
109
+ }
110
+
111
+ // Get full schema with columns and keys
112
+ {
113
+ "mode": "full"
114
+ }
115
+
116
+ // Filter specific tables
117
+ {
118
+ "mode": "full",
119
+ "filter": "users"
120
+ }
121
+ ```
122
+
123
+ **Response (summary):**
124
+
125
+ ```json
126
+ {
127
+ "mode": "summary",
128
+ "tables": [
129
+ {
130
+ "schema": "public",
131
+ "table": "users",
132
+ "approxRows": 1250
133
+ }
134
+ ]
135
+ }
136
+ ```
137
+
138
+ **Response (full):**
139
+
140
+ ```json
141
+ {
142
+ "mode": "full",
143
+ "schemas": {
144
+ "public": {
145
+ "users": {
146
+ "columns": [
147
+ {
148
+ "name": "id",
149
+ "dataType": "integer",
150
+ "udtName": "int4",
151
+ "nullable": false,
152
+ "default": "nextval('users_id_seq'::regclass)",
153
+ "position": 1
154
+ }
155
+ ],
156
+ "primaryKey": ["id"],
157
+ "foreignKeys": []
158
+ }
159
+ }
160
+ }
161
+ }
162
+ ```
163
+
164
+ ### 2. db.query
165
+
166
+ Execute a read-only SELECT query with optional parameters.
167
+
168
+ **Parameters:**
169
+
170
+ - `sql` (required): SELECT query (with or without LIMIT)
171
+ - `params` (optional): Array of parameter values for $1, $2, etc.
172
+ - `maxRows` (optional): Maximum rows to return (1-5000, default: 500)
173
+
174
+ **Examples:**
175
+
176
+ ```javascript
177
+ // Simple query
178
+ {
179
+ "sql": "SELECT * FROM users WHERE active = true"
180
+ }
181
+
182
+ // Parameterized query
183
+ {
184
+ "sql": "SELECT id, name, email FROM users WHERE country = $1 AND age > $2",
185
+ "params": ["USA", 25],
186
+ "maxRows": 100
187
+ }
188
+
189
+ // Query with existing LIMIT (will be honored if <= maxRows)
190
+ {
191
+ "sql": "SELECT * FROM orders ORDER BY created_at DESC LIMIT 10"
192
+ }
193
+ ```
194
+
195
+ **Response:**
196
+
197
+ ```json
198
+ {
199
+ "rowCount": 10,
200
+ "fields": ["id", "name", "email"],
201
+ "rows": [{ "id": 1, "name": "John Doe", "email": "john@example.com" }]
202
+ }
203
+ ```
204
+
205
+ **Security Notes:**
206
+
207
+ - Only SELECT and WITH (CTE) queries allowed
208
+ - Single statement only (no semicolons)
209
+ - Automatic LIMIT enforcement if not specified
210
+ - Query timeout: 5 seconds (default)
211
+
212
+ ### 3. db.preview
213
+
214
+ Quick preview of table rows.
215
+
216
+ **Parameters:**
217
+
218
+ - `table` (required): Table name (use `schema.table` or just `table`)
219
+ - `limit` (optional): Number of rows (1-500, default: 50)
220
+
221
+ **Examples:**
222
+
223
+ ```javascript
224
+ // Preview public.users table
225
+ {
226
+ "table": "users",
227
+ "limit": 20
228
+ }
229
+
230
+ // Preview from specific schema
231
+ {
232
+ "table": "analytics.events"
233
+ }
234
+ ```
235
+
236
+ **Response:**
237
+
238
+ ```json
239
+ {
240
+ "table": "public.users",
241
+ "rowCount": 20,
242
+ "rows": [{ "id": 1, "name": "Alice", "created_at": "2024-01-15T10:30:00Z" }]
243
+ }
244
+ ```
245
+
246
+ ### 4. db.watch
247
+
248
+ Poll for incremental changes using cursor-based pagination.
249
+
250
+ **Parameters:**
251
+
252
+ - `table` (required): Table name
253
+ - `cursorColumn` (optional): Column to track (default: `"updated_at"`)
254
+ - `lastCursor` (optional): Last cursor value from previous call
255
+ - `batchSize` (optional): Rows per batch (1-1000, default: 200)
256
+
257
+ **Examples:**
258
+
259
+ ```javascript
260
+ // Initial fetch (gets oldest records first)
261
+ {
262
+ "table": "orders",
263
+ "cursorColumn": "created_at"
264
+ }
265
+
266
+ // Subsequent fetch (pass lastCursor from previous response)
267
+ {
268
+ "table": "orders",
269
+ "cursorColumn": "created_at",
270
+ "lastCursor": "2024-01-15T14:23:45.123Z",
271
+ "batchSize": 100
272
+ }
273
+
274
+ // Track by numeric ID
275
+ {
276
+ "table": "logs",
277
+ "cursorColumn": "id",
278
+ "lastCursor": 5042
279
+ }
280
+ ```
281
+
282
+ **Response:**
283
+
284
+ ```json
285
+ {
286
+ "table": "public.orders",
287
+ "cursorColumn": "created_at",
288
+ "cursorType": "timestamp with time zone",
289
+ "lastCursor": "2024-01-15T15:30:00Z",
290
+ "rows": [...]
291
+ }
292
+ ```
293
+
294
+ **Use Case:**
295
+
296
+ - Real-time monitoring
297
+ - ETL/sync processes
298
+ - Audit log tracking
299
+ - Event streaming
300
+
301
+ ### 5. db.count
302
+
303
+ Get exact row count for a table.
304
+
305
+ **Parameters:**
306
+
307
+ - `table` (required): Table name (use `schema.table` or just `table`)
308
+
309
+ **Examples:**
310
+
311
+ ```javascript
312
+ // Count rows in public.users
313
+ {
314
+ "table": "users"
315
+ }
316
+
317
+ // Count in specific schema
318
+ {
319
+ "table": "analytics.pageviews"
320
+ }
321
+ ```
322
+
323
+ **Response:**
324
+
325
+ ```json
326
+ {
327
+ "table": "public.users",
328
+ "count": 15247
329
+ }
330
+ ```
331
+
332
+ ## Usage Examples
333
+
334
+ ### With Claude Desktop (MCP Client)
335
+
336
+ Add to your `claude_desktop_config.json`:
337
+
338
+ ```json
339
+ {
340
+ "mcpServers": {
341
+ "postgres": {
342
+ "command": "node",
343
+ "args": ["d:/code/node/pg-mcp-server/dist/server.js"],
344
+ "env": {
345
+ "DATABASE_URL": "postgres://user:pass@localhost:5432/mydb"
346
+ }
347
+ }
348
+ }
349
+ }
350
+ ```
351
+
352
+ Or if installed globally via npm:
353
+
354
+ ```json
355
+ {
356
+ "mcpServers": {
357
+ "postgres": {
358
+ "command": "postgres-mcp-readonly",
359
+ "env": {
360
+ "DATABASE_URL": "postgres://user:pass@localhost:5432/mydb"
361
+ }
362
+ }
363
+ }
364
+ }
365
+ ```
366
+
367
+ ### Example Conversation Flow
368
+
369
+ **User:** "Show me the database schema"
370
+
371
+ **AI uses:** `db.schema` with `mode: "summary"`
372
+
373
+ ---
374
+
375
+ **User:** "How many users do we have?"
376
+
377
+ **AI uses:** `db.count` with `table: "users"`
378
+
379
+ ---
380
+
381
+ **User:** "Show me the 10 most recent orders"
382
+
383
+ **AI uses:** `db.query` with SQL:
384
+
385
+ ```sql
386
+ SELECT * FROM orders ORDER BY created_at DESC LIMIT 10
387
+ ```
388
+
389
+ ---
390
+
391
+ **User:** "Watch for new signups"
392
+
393
+ **AI uses:** `db.watch` with `table: "users"`, `cursorColumn: "created_at"`
394
+
395
+ ## Security Features
396
+
397
+ ### Query Validation
398
+
399
+ The server performs multiple security checks:
400
+
401
+ 1. **Keyword Blocklist** - Prevents: INSERT, UPDATE, DELETE, DROP, ALTER, CREATE, TRUNCATE, GRANT, REVOKE, VACUUM, ANALYZE, REINDEX, COPY, CALL, DO, EXECUTE
402
+
403
+ 2. **Comment Stripping** - Removes SQL comments to prevent obfuscation
404
+
405
+ 3. **Single Statement** - Only one query per request (no semicolons)
406
+
407
+ 4. **SELECT-only** - Must start with SELECT or WITH
408
+
409
+ 5. **Identifier Validation** - Table/column names must match `[a-zA-Z_][a-zA-Z0-9_]*`
410
+
411
+ 6. **Parameterization** - Supports bind parameters ($1, $2, etc.) to prevent injection
412
+
413
+ ### Error Sanitization
414
+
415
+ Database errors are sanitized to prevent leaking:
416
+
417
+ - Connection strings and passwords
418
+ - Server hostnames
419
+ - File system paths
420
+ - Overly verbose stack traces
421
+
422
+ ### Connection Safety
423
+
424
+ - **Connection pooling** with max 10 connections
425
+ - **Statement timeout** (5s default) prevents runaway queries
426
+ - **Lock timeout** (1s) prevents deadlock situations
427
+ - **Idle transaction timeout** (5s) frees stuck connections
428
+ - **Graceful shutdown** on SIGINT/SIGTERM
429
+
430
+ ## Best Practices
431
+
432
+ ### For AI Assistants
433
+
434
+ 1. **Always check schema first** - Use `db.schema` before querying unknown tables
435
+ 2. **Use parameterization** - Never concatenate user input into SQL strings
436
+ 3. **Start with small limits** - Use low `maxRows` for exploratory queries
437
+ 4. **Use db.count for totals** - Don't SELECT COUNT(\*) manually
438
+ 5. **Handle errors gracefully** - Sanitized errors are safe to show users
439
+
440
+ ### For Database Admins
441
+
442
+ 1. **Use read-only database user** - Grant only SELECT permissions
443
+ 2. **Monitor connection usage** - Set appropriate pool size
444
+ 3. **Adjust timeouts** - Based on your query complexity
445
+ 4. **Enable query logging** - In PostgreSQL for audit trail
446
+ 5. **Use SSL connections** - Add `?sslmode=require` to DATABASE_URL
447
+
448
+ ### Performance Tips
449
+
450
+ 1. **Ensure indexed columns** - Especially for `db.watch` cursor columns
451
+ 2. **Use filters in db.schema** - Don't fetch full schema repeatedly
452
+ 3. **Keep maxRows reasonable** - Large result sets slow serialization
453
+ 4. **Add indexes on sort columns** - For ORDER BY performance
454
+
455
+ ## Troubleshooting
456
+
457
+ ### Connection Issues
458
+
459
+ **Problem:** `Missing DATABASE_URL` error
460
+
461
+ **Solution:** Create `.env` file with valid connection string
462
+
463
+ ---
464
+
465
+ **Problem:** `ECONNREFUSED` or connection timeout
466
+
467
+ **Solution:**
468
+
469
+ - Verify PostgreSQL is running
470
+ - Check host/port in DATABASE_URL
471
+ - Ensure firewall allows connections
472
+ - Test with `psql` command line first
473
+
474
+ ---
475
+
476
+ **Problem:** `password authentication failed`
477
+
478
+ **Solution:** Verify username/password in DATABASE_URL
479
+
480
+ ### Query Errors
481
+
482
+ **Problem:** `Blocked keyword detected: insert`
483
+
484
+ **Solution:** This is intentional - only SELECT queries are allowed
485
+
486
+ ---
487
+
488
+ **Problem:** `Only SELECT queries are allowed`
489
+
490
+ **Solution:** Ensure query starts with SELECT or WITH, not EXPLAIN, SHOW, etc.
491
+
492
+ ---
493
+
494
+ **Problem:** `statement timeout`
495
+
496
+ **Solution:**
497
+
498
+ - Increase STATEMENT_TIMEOUT_MS
499
+ - Optimize query with indexes
500
+ - Reduce dataset with WHERE clause
501
+
502
+ ### Schema Issues
503
+
504
+ **Problem:** `relation "table_name" does not exist`
505
+
506
+ **Solution:**
507
+
508
+ - Check table name spelling
509
+ - Use `schema.table` if not in `public` schema
510
+ - Run `db.schema` to see available tables
511
+
512
+ ## Development
513
+
514
+ ### Running Locally
515
+
516
+ ```bash
517
+ # Set environment variables
518
+ export DATABASE_URL="postgres://localhost:5432/testdb"
519
+
520
+ # Build and run server
521
+ npm run build
522
+ npm start
523
+
524
+ # Or use dev mode (builds and runs)
525
+ npm run dev
526
+ ```
527
+
528
+ ### Testing with MCP Inspector
529
+
530
+ ```bash
531
+ # Build first
532
+ npm run build
533
+
534
+ # Then inspect
535
+ npx @modelcontextprotocol/inspector node dist/server.js
536
+ ```
537
+
538
+ ## License
539
+
540
+ MIT
541
+
542
+ ## Contributing
543
+
544
+ Contributions welcome! Please ensure:
545
+
546
+ - Security best practices maintained
547
+ - All tools remain read-only
548
+ - Tests pass (if added)
549
+ - Documentation updated
550
+
551
+ ## Support
552
+
553
+ For issues or questions:
554
+
555
+ 1. Check this README first
556
+ 2. Review PostgreSQL connection docs
557
+ 3. Test with `psql` to isolate database issues
558
+ 4. Open an issue with sanitized error messages
559
+
560
+ ---
561
+
562
+ **Remember:** This server is read-only by design. For database modifications, use traditional database tools or separate admin interfaces.
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":""}
package/dist/server.js ADDED
@@ -0,0 +1,503 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
8
+ const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
9
+ const dotenv_1 = __importDefault(require("dotenv"));
10
+ const pg_1 = require("pg");
11
+ const zod_1 = require("zod");
12
+ dotenv_1.default.config({ quiet: true });
13
+ function getDatabaseUrl() {
14
+ const value = (process.env.DATABASE_URL || "").trim();
15
+ if (!value) {
16
+ throw new Error("Missing DATABASE_URL. Example: postgres://user:password@localhost:5432/postgres");
17
+ }
18
+ let parsed;
19
+ try {
20
+ parsed = new URL(value);
21
+ }
22
+ catch {
23
+ throw new Error("Invalid DATABASE_URL format. Expected: postgres://user:password@host:5432/dbname");
24
+ }
25
+ if (!["postgres:", "postgresql:"].includes(parsed.protocol)) {
26
+ throw new Error("DATABASE_URL must start with postgres:// or postgresql://");
27
+ }
28
+ return value;
29
+ }
30
+ const DATABASE_URL = getDatabaseUrl();
31
+ const STATEMENT_TIMEOUT_MS = Number(process.env.STATEMENT_TIMEOUT_MS || 5000);
32
+ const DEFAULT_MAX_ROWS = Number(process.env.MAX_ROWS || 500);
33
+ const pool = new pg_1.Pool({
34
+ connectionString: DATABASE_URL,
35
+ max: 10,
36
+ });
37
+ async function withClient(fn) {
38
+ const client = await pool.connect();
39
+ try {
40
+ await client.query(`SET statement_timeout = '${STATEMENT_TIMEOUT_MS}ms'`);
41
+ await client.query("SET lock_timeout = '1000ms'");
42
+ await client.query("SET idle_in_transaction_session_timeout = '5000ms'");
43
+ return await fn(client);
44
+ }
45
+ finally {
46
+ client.release();
47
+ }
48
+ }
49
+ const BLOCKLIST = [
50
+ "insert",
51
+ "update",
52
+ "delete",
53
+ "drop",
54
+ "alter",
55
+ "create",
56
+ "truncate",
57
+ "grant",
58
+ "revoke",
59
+ "vacuum",
60
+ "analyze",
61
+ "reindex",
62
+ "copy",
63
+ "call",
64
+ "do",
65
+ "execute",
66
+ ];
67
+ function normalizeSql(sql) {
68
+ return sql
69
+ .replace(/--.*$/gm, "")
70
+ .replace(/\/\*[\s\S]*?\*\//g, "")
71
+ .trim();
72
+ }
73
+ function assertSelectOnly(sql) {
74
+ const s = normalizeSql(sql).toLowerCase();
75
+ if (s.includes(";")) {
76
+ throw new Error("Only single-statement queries are allowed.");
77
+ }
78
+ if (!s.startsWith("select") && !s.startsWith("with")) {
79
+ throw new Error("Only SELECT queries are allowed.");
80
+ }
81
+ for (const bad of BLOCKLIST) {
82
+ if (new RegExp(`\\b${bad}\\b`, "i").test(s)) {
83
+ throw new Error(`Blocked keyword detected: ${bad}`);
84
+ }
85
+ }
86
+ }
87
+ function enforceLimit(sql, maxRows = DEFAULT_MAX_ROWS) {
88
+ const s = normalizeSql(sql);
89
+ if (!/\blimit\b/i.test(s)) {
90
+ return `${s} LIMIT ${maxRows}`;
91
+ }
92
+ // If LIMIT exists, extract it and ensure it doesn't exceed maxRows
93
+ const limitMatch = s.match(/\blimit\s+(\d+)/i);
94
+ if (limitMatch) {
95
+ const existingLimit = parseInt(limitMatch[1], 10);
96
+ if (existingLimit <= maxRows) {
97
+ return s; // Trust existing LIMIT if within bounds
98
+ }
99
+ }
100
+ // Wrap query to enforce maxRows if existing LIMIT exceeds it
101
+ return `SELECT * FROM (${s}) AS _q LIMIT ${maxRows}`;
102
+ }
103
+ function sanitizeError(error) {
104
+ // Sanitize database errors to prevent information leakage
105
+ const message = error.message || String(error);
106
+ // Remove connection details, file paths, and sensitive info
107
+ let sanitized = message
108
+ .replace(/\b(?:password|pwd|secret|token|key)\s*=\s*[^\s;]*/gi, "[REDACTED]")
109
+ .replace(/\b(?:host|server)\s*=\s*[^\s;,]*/gi, "[HOST]")
110
+ .replace(/[A-Za-z]:\\[^\s"]*/g, "[PATH]")
111
+ .replace(/\/(?:home|usr|var)\/[^\s"]*/g, "[PATH]");
112
+ // Preserve common PostgreSQL error patterns that are safe
113
+ const safePatterns = [
114
+ /column "[^"]+" does not exist/i,
115
+ /relation "[^"]+" does not exist/i,
116
+ /syntax error/i,
117
+ /permission denied/i,
118
+ /statement timeout/i,
119
+ /lock timeout/i,
120
+ ];
121
+ const isSafe = safePatterns.some((pattern) => pattern.test(message));
122
+ if (!isSafe && message.length > 200) {
123
+ sanitized = sanitized.substring(0, 200) + "...";
124
+ }
125
+ const sanitizedError = new Error(sanitized);
126
+ sanitizedError.name = error.name || "DatabaseError";
127
+ return sanitizedError;
128
+ }
129
+ function assertIdentifier(name) {
130
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {
131
+ throw new Error(`Invalid identifier: ${name}`);
132
+ }
133
+ }
134
+ function parseTableName(input) {
135
+ const parts = input.split(".");
136
+ if (parts.length === 1) {
137
+ assertIdentifier(parts[0]);
138
+ return { schema: "public", table: parts[0] };
139
+ }
140
+ if (parts.length === 2) {
141
+ assertIdentifier(parts[0]);
142
+ assertIdentifier(parts[1]);
143
+ return { schema: parts[0], table: parts[1] };
144
+ }
145
+ throw new Error("Invalid table name format. Use table or schema.table");
146
+ }
147
+ async function dbSchema({ mode = "summary", filter = "", } = {}) {
148
+ const Mode = zod_1.z.enum(["summary", "full"]);
149
+ Mode.parse(mode);
150
+ const filterLike = `%${filter}%`;
151
+ return withClient(async (client) => {
152
+ const tablesRes = await client.query(`
153
+ SELECT table_schema, table_name
154
+ FROM information_schema.tables
155
+ WHERE table_type='BASE TABLE'
156
+ AND table_schema NOT IN ('pg_catalog','information_schema')
157
+ AND ($1 = '%%' OR table_name ILIKE $1 OR table_schema ILIKE $1)
158
+ ORDER BY table_schema, table_name
159
+ `, [filter ? filterLike : "%%"]);
160
+ if (mode === "summary") {
161
+ const countsRes = await client.query(`
162
+ SELECT schemaname AS table_schema, relname AS table_name, n_live_tup::bigint AS approx_rows
163
+ FROM pg_stat_user_tables
164
+ `);
165
+ const countMap = new Map(countsRes.rows.map((row) => [
166
+ `${row.table_schema}.${row.table_name}`,
167
+ row.approx_rows,
168
+ ]));
169
+ return {
170
+ mode: "summary",
171
+ tables: tablesRes.rows.map((row) => ({
172
+ schema: row.table_schema,
173
+ table: row.table_name,
174
+ approxRows: countMap.get(`${row.table_schema}.${row.table_name}`) ?? null,
175
+ })),
176
+ };
177
+ }
178
+ const colsRes = await client.query(`
179
+ SELECT
180
+ c.table_schema,
181
+ c.table_name,
182
+ c.ordinal_position,
183
+ c.column_name,
184
+ c.data_type,
185
+ c.udt_name,
186
+ c.is_nullable,
187
+ c.column_default
188
+ FROM information_schema.columns c
189
+ WHERE c.table_schema NOT IN ('pg_catalog','information_schema')
190
+ AND ($1 = '%%' OR c.table_name ILIKE $1 OR c.table_schema ILIKE $1)
191
+ ORDER BY c.table_schema, c.table_name, c.ordinal_position
192
+ `, [filter ? filterLike : "%%"]);
193
+ const pkRes = await client.query(`
194
+ SELECT
195
+ tc.table_schema,
196
+ tc.table_name,
197
+ kcu.column_name,
198
+ kcu.ordinal_position
199
+ FROM information_schema.table_constraints tc
200
+ JOIN information_schema.key_column_usage kcu
201
+ ON tc.constraint_name = kcu.constraint_name
202
+ AND tc.table_schema = kcu.table_schema
203
+ WHERE tc.constraint_type = 'PRIMARY KEY'
204
+ AND tc.table_schema NOT IN ('pg_catalog','information_schema')
205
+ AND ($1 = '%%' OR tc.table_name ILIKE $1 OR tc.table_schema ILIKE $1)
206
+ ORDER BY tc.table_schema, tc.table_name, kcu.ordinal_position
207
+ `, [filter ? filterLike : "%%"]);
208
+ const fkRes = await client.query(`
209
+ SELECT
210
+ tc.table_schema,
211
+ tc.table_name,
212
+ kcu.column_name,
213
+ ccu.table_schema AS foreign_table_schema,
214
+ ccu.table_name AS foreign_table_name,
215
+ ccu.column_name AS foreign_column_name
216
+ FROM information_schema.table_constraints tc
217
+ JOIN information_schema.key_column_usage kcu
218
+ ON tc.constraint_name = kcu.constraint_name
219
+ AND tc.table_schema = kcu.table_schema
220
+ JOIN information_schema.constraint_column_usage ccu
221
+ ON ccu.constraint_name = tc.constraint_name
222
+ AND ccu.table_schema = tc.table_schema
223
+ WHERE tc.constraint_type = 'FOREIGN KEY'
224
+ AND tc.table_schema NOT IN ('pg_catalog','information_schema')
225
+ AND ($1 = '%%' OR tc.table_name ILIKE $1 OR tc.table_schema ILIKE $1)
226
+ ORDER BY tc.table_schema, tc.table_name
227
+ `, [filter ? filterLike : "%%"]);
228
+ const out = { mode: "full", schemas: {} };
229
+ for (const row of tablesRes.rows) {
230
+ out.schemas[row.table_schema] ??= {};
231
+ out.schemas[row.table_schema][row.table_name] = {
232
+ columns: [],
233
+ primaryKey: [],
234
+ foreignKeys: [],
235
+ };
236
+ }
237
+ for (const row of colsRes.rows) {
238
+ const table = out.schemas?.[row.table_schema]?.[row.table_name];
239
+ if (!table) {
240
+ continue;
241
+ }
242
+ table.columns.push({
243
+ name: row.column_name,
244
+ dataType: row.data_type,
245
+ udtName: row.udt_name,
246
+ nullable: row.is_nullable === "YES",
247
+ default: row.column_default,
248
+ position: row.ordinal_position,
249
+ });
250
+ }
251
+ for (const row of pkRes.rows) {
252
+ const table = out.schemas?.[row.table_schema]?.[row.table_name];
253
+ if (!table) {
254
+ continue;
255
+ }
256
+ table.primaryKey.push(row.column_name);
257
+ }
258
+ for (const row of fkRes.rows) {
259
+ const table = out.schemas?.[row.table_schema]?.[row.table_name];
260
+ if (!table) {
261
+ continue;
262
+ }
263
+ table.foreignKeys.push({
264
+ column: row.column_name,
265
+ refSchema: row.foreign_table_schema,
266
+ refTable: row.foreign_table_name,
267
+ refColumn: row.foreign_column_name,
268
+ });
269
+ }
270
+ return out;
271
+ });
272
+ }
273
+ async function dbQuery({ sql, params = [], maxRows = DEFAULT_MAX_ROWS, }) {
274
+ zod_1.z.object({
275
+ sql: zod_1.z.string().min(1),
276
+ params: zod_1.z.array(zod_1.z.any()).optional(),
277
+ maxRows: zod_1.z.number().int().min(1).max(5000).optional(),
278
+ }).parse({ sql, params, maxRows });
279
+ assertSelectOnly(sql);
280
+ const safeSql = enforceLimit(sql, maxRows);
281
+ return withClient(async (client) => {
282
+ await client.query("BEGIN READ ONLY");
283
+ try {
284
+ const result = await client.query(safeSql, params);
285
+ await client.query("COMMIT");
286
+ return {
287
+ rowCount: result.rowCount || 0,
288
+ fields: result.fields.map((field) => field.name),
289
+ rows: result.rows,
290
+ };
291
+ }
292
+ catch (error) {
293
+ await client.query("ROLLBACK");
294
+ throw sanitizeError(error);
295
+ }
296
+ });
297
+ }
298
+ async function dbPreview({ table, limit = 50, }) {
299
+ zod_1.z.object({
300
+ table: zod_1.z.string().min(1),
301
+ limit: zod_1.z.number().int().min(1).max(500).optional(),
302
+ }).parse({ table, limit });
303
+ const parsed = parseTableName(table);
304
+ return withClient(async (client) => {
305
+ await client.query("BEGIN READ ONLY");
306
+ try {
307
+ const result = await client.query(`SELECT * FROM "${parsed.schema}"."${parsed.table}" LIMIT $1`, [limit]);
308
+ await client.query("COMMIT");
309
+ return {
310
+ table: `${parsed.schema}.${parsed.table}`,
311
+ rowCount: result.rowCount || 0,
312
+ rows: result.rows,
313
+ };
314
+ }
315
+ catch (error) {
316
+ await client.query("ROLLBACK");
317
+ throw sanitizeError(error);
318
+ }
319
+ });
320
+ }
321
+ async function dbWatch({ table, cursorColumn = "updated_at", lastCursor = null, batchSize = 200, }) {
322
+ zod_1.z.object({
323
+ table: zod_1.z.string().min(1),
324
+ cursorColumn: zod_1.z.string().min(1).optional(),
325
+ lastCursor: zod_1.z.union([zod_1.z.string(), zod_1.z.number(), zod_1.z.null()]).optional(),
326
+ batchSize: zod_1.z.number().int().min(1).max(1000).optional(),
327
+ }).parse({ table, cursorColumn, lastCursor, batchSize });
328
+ const parsed = parseTableName(table);
329
+ assertIdentifier(cursorColumn);
330
+ const cursorType = await withClient(async (client) => {
331
+ const res = await client.query(`
332
+ SELECT data_type
333
+ FROM information_schema.columns
334
+ WHERE table_schema = $1 AND table_name = $2 AND column_name = $3
335
+ `, [parsed.schema, parsed.table, cursorColumn]);
336
+ if (res.rows.length === 0) {
337
+ throw new Error(`Cursor column not found: ${cursorColumn}`);
338
+ }
339
+ return res.rows[0].data_type;
340
+ });
341
+ const isTimestamp = /timestamp|date|time/i.test(cursorType);
342
+ const effectiveCursor = lastCursor !== null && lastCursor !== undefined
343
+ ? lastCursor
344
+ : isTimestamp
345
+ ? "1970-01-01T00:00:00.000Z"
346
+ : 0;
347
+ return withClient(async (client) => {
348
+ await client.query("BEGIN READ ONLY");
349
+ try {
350
+ const sql = `
351
+ SELECT *
352
+ FROM "${parsed.schema}"."${parsed.table}"
353
+ WHERE "${cursorColumn}" > $1
354
+ ORDER BY "${cursorColumn}" ASC
355
+ LIMIT $2
356
+ `;
357
+ const result = await client.query(sql, [effectiveCursor, batchSize]);
358
+ await client.query("COMMIT");
359
+ const nextCursor = result.rows.length > 0
360
+ ? result.rows[result.rows.length - 1][cursorColumn]
361
+ : effectiveCursor;
362
+ return {
363
+ table: `${parsed.schema}.${parsed.table}`,
364
+ cursorColumn,
365
+ cursorType,
366
+ lastCursor: nextCursor,
367
+ rows: result.rows,
368
+ };
369
+ }
370
+ catch (error) {
371
+ await client.query("ROLLBACK");
372
+ throw sanitizeError(error);
373
+ }
374
+ });
375
+ }
376
+ async function dbCount({ table, }) {
377
+ zod_1.z.object({
378
+ table: zod_1.z.string().min(1),
379
+ }).parse({ table });
380
+ const parsed = parseTableName(table);
381
+ return withClient(async (client) => {
382
+ await client.query("BEGIN READ ONLY");
383
+ try {
384
+ const result = await client.query(`SELECT COUNT(*) AS count FROM "${parsed.schema}"."${parsed.table}"`);
385
+ await client.query("COMMIT");
386
+ return {
387
+ table: `${parsed.schema}.${parsed.table}`,
388
+ count: parseInt(result.rows[0].count, 10),
389
+ };
390
+ }
391
+ catch (error) {
392
+ await client.query("ROLLBACK");
393
+ throw sanitizeError(error);
394
+ }
395
+ });
396
+ }
397
+ function asTextContent(payload) {
398
+ return {
399
+ content: [
400
+ {
401
+ type: "text",
402
+ text: JSON.stringify(payload, null, 2),
403
+ },
404
+ ],
405
+ structuredContent: payload,
406
+ };
407
+ }
408
+ const server = new mcp_js_1.McpServer({
409
+ name: "pg_saga_db",
410
+ version: "1.0.0",
411
+ });
412
+ server.registerTool("db.schema", {
413
+ description: "Inspect database schema. Use mode='summary' for table list or mode='full' for columns and keys.",
414
+ inputSchema: {
415
+ mode: zod_1.z.enum(["summary", "full"]).optional(),
416
+ filter: zod_1.z.string().optional(),
417
+ },
418
+ }, async ({ mode = "summary", filter = "" }) => asTextContent(await dbSchema({ mode, filter })));
419
+ server.registerTool("db.query", {
420
+ description: "Run a single read-only SELECT query with optional parameters and row limit.",
421
+ inputSchema: {
422
+ sql: zod_1.z.string().min(1),
423
+ params: zod_1.z.array(zod_1.z.any()).optional(),
424
+ maxRows: zod_1.z.number().int().min(1).max(5000).optional(),
425
+ },
426
+ }, async ({ sql, params = [], maxRows = DEFAULT_MAX_ROWS }) => asTextContent(await dbQuery({ sql, params, maxRows })));
427
+ server.registerTool("db.preview", {
428
+ description: "Preview rows from a table using table or schema.table name.",
429
+ inputSchema: {
430
+ table: zod_1.z.string().min(1),
431
+ limit: zod_1.z.number().int().min(1).max(500).optional(),
432
+ },
433
+ }, async ({ table, limit = 50 }) => asTextContent(await dbPreview({ table, limit })));
434
+ server.registerTool("db.watch", {
435
+ description: "Fetch one incremental batch where cursorColumn > lastCursor. Repeat client-side for polling.",
436
+ inputSchema: {
437
+ table: zod_1.z.string().min(1),
438
+ cursorColumn: zod_1.z.string().min(1).optional(),
439
+ lastCursor: zod_1.z.union([zod_1.z.string(), zod_1.z.number(), zod_1.z.null()]).optional(),
440
+ batchSize: zod_1.z.number().int().min(1).max(1000).optional(),
441
+ },
442
+ }, async ({ table, cursorColumn = "updated_at", lastCursor = null, batchSize = 200, }) => asTextContent(await dbWatch({ table, cursorColumn, lastCursor, batchSize })));
443
+ server.registerTool("db.count", {
444
+ description: "Get exact row count for a table. Use table or schema.table name.",
445
+ inputSchema: {
446
+ table: zod_1.z.string().min(1),
447
+ },
448
+ }, async ({ table }) => asTextContent(await dbCount({ table })));
449
+ const SCHEMA_SUMMARY_URI = "pg://schema/summary";
450
+ const SCHEMA_FULL_URI = "pg://schema/full";
451
+ server.registerResource("schema-summary", SCHEMA_SUMMARY_URI, {
452
+ mimeType: "application/json",
453
+ description: "Summary of tables and approximate row counts.",
454
+ }, async () => {
455
+ const payload = await dbSchema({ mode: "summary" });
456
+ return {
457
+ contents: [
458
+ {
459
+ uri: SCHEMA_SUMMARY_URI,
460
+ mimeType: "application/json",
461
+ text: JSON.stringify(payload, null, 2),
462
+ },
463
+ ],
464
+ };
465
+ });
466
+ server.registerResource("schema-full", SCHEMA_FULL_URI, {
467
+ mimeType: "application/json",
468
+ description: "Full schema including columns, primary keys, and foreign keys.",
469
+ }, async () => {
470
+ const payload = await dbSchema({ mode: "full" });
471
+ return {
472
+ contents: [
473
+ {
474
+ uri: SCHEMA_FULL_URI,
475
+ mimeType: "application/json",
476
+ text: JSON.stringify(payload, null, 2),
477
+ },
478
+ ],
479
+ };
480
+ });
481
+ async function closeResources() {
482
+ try {
483
+ await server.close();
484
+ }
485
+ finally {
486
+ await pool.end();
487
+ }
488
+ }
489
+ process.on("SIGINT", () => {
490
+ closeResources().finally(() => process.exit(0));
491
+ });
492
+ process.on("SIGTERM", () => {
493
+ closeResources().finally(() => process.exit(0));
494
+ });
495
+ async function main() {
496
+ const transport = new stdio_js_1.StdioServerTransport();
497
+ await server.connect(transport);
498
+ }
499
+ main().catch((error) => {
500
+ console.error("pg_saga_db MCP server error:", error);
501
+ process.exit(1);
502
+ });
503
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "postgres-mcp-readonly",
3
+ "version": "1.0.0",
4
+ "description": "A secure, read-only PostgreSQL Model Context Protocol (MCP) server for safe database introspection and querying",
5
+ "main": "dist/server.js",
6
+ "types": "dist/server.d.ts",
7
+ "bin": {
8
+ "postgres-mcp-readonly": "./dist/server.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "build:watch": "tsc --watch",
13
+ "start": "node dist/server.js",
14
+ "dev": "tsc && node dist/server.js",
15
+ "prepublishOnly": "npm run build",
16
+ "clean": "rimraf dist",
17
+ "test": "echo \"Error: no test specified\" && exit 1"
18
+ },
19
+ "keywords": [
20
+ "mcp",
21
+ "model-context-protocol",
22
+ "postgresql",
23
+ "postgres",
24
+ "database",
25
+ "read-only",
26
+ "ai",
27
+ "llm",
28
+ "claude",
29
+ "typescript"
30
+ ],
31
+ "author": "Mohaimanul islam mahin mohaimanul6@gmail.com",
32
+ "license": "MIT",
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "https://github.com/mahin1995/postgres-mcp-readonly.git"
36
+ },
37
+ "bugs": {
38
+ "url": "https://github.com/mahin1995/postgres-mcp-readonly/issues"
39
+ },
40
+ "homepage": "https://github.com/mahin1995/postgres-mcp-readonly#readme",
41
+ "engines": {
42
+ "node": ">=18.0.0"
43
+ },
44
+ "files": [
45
+ "dist",
46
+ "README.md",
47
+ "LICENSE"
48
+ ],
49
+ "dependencies": {
50
+ "@modelcontextprotocol/sdk": "^1.27.1",
51
+ "dotenv": "^17.3.1",
52
+ "pg": "^8.19.0",
53
+ "zod": "^4.3.6"
54
+ },
55
+ "devDependencies": {
56
+ "@types/node": "^20.0.0",
57
+ "@types/pg": "^8.16.0",
58
+ "rimraf": "^5.0.5",
59
+ "typescript": "^5.3.0"
60
+ }
61
+ }