@spec-this/mcp-postgres-sql 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/README.md +183 -0
- package/dist/index.js +394 -0
- package/package.json +55 -0
package/README.md
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# MCP PostgreSQL
|
|
2
|
+
|
|
3
|
+
A Model Context Protocol (MCP) server that provides PostgreSQL database query capabilities with built-in connection management.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- PostgreSQL database support using the `pg` driver
|
|
8
|
+
- Connection management via MCP tools (add, list, update, remove)
|
|
9
|
+
- Connections stored in `~/.mcp-postgres-sql/connections.json`
|
|
10
|
+
- No environment variables needed — manage connections through the tools
|
|
11
|
+
- Passwords hidden when listing connections
|
|
12
|
+
- SSL support
|
|
13
|
+
- Stdio transport for MCP integration
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install
|
|
19
|
+
npm run build
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## MCP Configuration
|
|
23
|
+
|
|
24
|
+
Add to your Claude Desktop or Claude Code MCP config:
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"mcpServers": {
|
|
29
|
+
"postgres": {
|
|
30
|
+
"command": "node",
|
|
31
|
+
"args": ["/path/to/mcp-postgres-sql/dist/index.js"]
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Or if installed globally:
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"mcpServers": {
|
|
42
|
+
"postgres": {
|
|
43
|
+
"command": "mcp-postgres-sql"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Tools
|
|
50
|
+
|
|
51
|
+
### `add_connection`
|
|
52
|
+
|
|
53
|
+
Save a new PostgreSQL connection configuration.
|
|
54
|
+
|
|
55
|
+
| Parameter | Type | Required | Default | Description |
|
|
56
|
+
|------------|---------|----------|---------|--------------------|
|
|
57
|
+
| `name` | string | yes | | Name for this connection |
|
|
58
|
+
| `host` | string | yes | | Database host |
|
|
59
|
+
| `port` | number | no | 5432 | Database port |
|
|
60
|
+
| `database` | string | yes | | Database name |
|
|
61
|
+
| `user` | string | yes | | Database user |
|
|
62
|
+
| `password` | string | yes | | Database password |
|
|
63
|
+
| `ssl` | boolean | no | false | Use SSL connection |
|
|
64
|
+
|
|
65
|
+
### `list_connections`
|
|
66
|
+
|
|
67
|
+
List all saved connections. Passwords are hidden from the output.
|
|
68
|
+
|
|
69
|
+
### `update_connection`
|
|
70
|
+
|
|
71
|
+
Update an existing connection. Only the fields you provide will be changed.
|
|
72
|
+
|
|
73
|
+
| Parameter | Type | Required | Description |
|
|
74
|
+
|------------|---------|----------|--------------------------|
|
|
75
|
+
| `name` | string | yes | Connection name to update |
|
|
76
|
+
| `host` | string | no | New host |
|
|
77
|
+
| `port` | number | no | New port |
|
|
78
|
+
| `database` | string | no | New database name |
|
|
79
|
+
| `user` | string | no | New user |
|
|
80
|
+
| `password` | string | no | New password |
|
|
81
|
+
| `ssl` | boolean | no | New SSL setting |
|
|
82
|
+
|
|
83
|
+
### `remove_connection`
|
|
84
|
+
|
|
85
|
+
Remove a saved connection. If this is the active connection, it will be disconnected first.
|
|
86
|
+
|
|
87
|
+
| Parameter | Type | Required | Description |
|
|
88
|
+
|-----------|--------|----------|--------------------------|
|
|
89
|
+
| `name` | string | yes | Connection name to remove |
|
|
90
|
+
|
|
91
|
+
### `connect`
|
|
92
|
+
|
|
93
|
+
Connect to a saved connection by name. Disconnects any existing connection first.
|
|
94
|
+
|
|
95
|
+
| Parameter | Type | Required | Description |
|
|
96
|
+
|-----------|--------|----------|------------------------------|
|
|
97
|
+
| `name` | string | yes | Connection name to connect to |
|
|
98
|
+
|
|
99
|
+
### `disconnect`
|
|
100
|
+
|
|
101
|
+
Disconnect from the current database.
|
|
102
|
+
|
|
103
|
+
### `query`
|
|
104
|
+
|
|
105
|
+
Execute a SQL query against the connected database.
|
|
106
|
+
|
|
107
|
+
| Parameter | Type | Required | Description |
|
|
108
|
+
|-----------|--------|----------|----------------------|
|
|
109
|
+
| `sql` | string | yes | The SQL query to run |
|
|
110
|
+
|
|
111
|
+
Returns JSON-formatted query results.
|
|
112
|
+
|
|
113
|
+
## Usage Example
|
|
114
|
+
|
|
115
|
+
Once configured as an MCP server, use the tools in sequence:
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
1. add_connection → name: "local-dev", host: "localhost", database: "myapp", user: "postgres", password: "secret"
|
|
119
|
+
2. connect → name: "local-dev"
|
|
120
|
+
3. query → sql: "SELECT * FROM users LIMIT 10"
|
|
121
|
+
4. disconnect
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Connection Storage
|
|
125
|
+
|
|
126
|
+
Connections are stored at `~/.mcp-postgres-sql/connections.json`:
|
|
127
|
+
|
|
128
|
+
```json
|
|
129
|
+
{
|
|
130
|
+
"connections": {
|
|
131
|
+
"local-dev": {
|
|
132
|
+
"host": "localhost",
|
|
133
|
+
"port": 5432,
|
|
134
|
+
"database": "myapp",
|
|
135
|
+
"user": "postgres",
|
|
136
|
+
"password": "secret",
|
|
137
|
+
"ssl": false
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
The directory is created with `0700` permissions and the file with `0600` permissions.
|
|
144
|
+
|
|
145
|
+
## Development
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
# Install dependencies
|
|
149
|
+
npm install
|
|
150
|
+
|
|
151
|
+
# Run in development mode
|
|
152
|
+
npm run dev
|
|
153
|
+
|
|
154
|
+
# Build for production
|
|
155
|
+
npm run build
|
|
156
|
+
|
|
157
|
+
# Type check
|
|
158
|
+
npx tsc --noEmit
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Testing
|
|
162
|
+
|
|
163
|
+
Tests require Docker.
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
# Start PostgreSQL test container
|
|
167
|
+
npm run test:up
|
|
168
|
+
|
|
169
|
+
# Run tests
|
|
170
|
+
npm test
|
|
171
|
+
|
|
172
|
+
# Stop and clean up
|
|
173
|
+
npm run test:down
|
|
174
|
+
|
|
175
|
+
# Or run everything at once
|
|
176
|
+
npm run test:ci
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
The test container runs PostgreSQL 16 on port 5433 to avoid conflicts with any local instance.
|
|
180
|
+
|
|
181
|
+
## License
|
|
182
|
+
|
|
183
|
+
MIT
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import pg from "pg";
|
|
6
|
+
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
7
|
+
import { join } from "path";
|
|
8
|
+
import { homedir } from "os";
|
|
9
|
+
import { existsSync } from "fs";
|
|
10
|
+
const { Client } = pg;
|
|
11
|
+
class ConnectionManager {
|
|
12
|
+
configPath;
|
|
13
|
+
constructor() {
|
|
14
|
+
const configDir = join(homedir(), ".mcp-postgres-sql");
|
|
15
|
+
this.configPath = join(configDir, "connections.json");
|
|
16
|
+
}
|
|
17
|
+
async ensureConfigDir() {
|
|
18
|
+
const configDir = join(homedir(), ".mcp-postgres-sql");
|
|
19
|
+
if (!existsSync(configDir)) {
|
|
20
|
+
await mkdir(configDir, { recursive: true, mode: 0o700 });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
async loadConnections() {
|
|
24
|
+
await this.ensureConfigDir();
|
|
25
|
+
if (!existsSync(this.configPath)) {
|
|
26
|
+
return { connections: {} };
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
const data = await readFile(this.configPath, "utf-8");
|
|
30
|
+
return JSON.parse(data);
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
console.error("Error loading connections:", error);
|
|
34
|
+
return { connections: {} };
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async saveConnections(data) {
|
|
38
|
+
await this.ensureConfigDir();
|
|
39
|
+
await writeFile(this.configPath, JSON.stringify(data, null, 2), {
|
|
40
|
+
encoding: "utf-8",
|
|
41
|
+
mode: 0o600,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
async addConnection(name, config) {
|
|
45
|
+
const data = await this.loadConnections();
|
|
46
|
+
data.connections[name] = config;
|
|
47
|
+
await this.saveConnections(data);
|
|
48
|
+
}
|
|
49
|
+
async updateConnection(name, config) {
|
|
50
|
+
const data = await this.loadConnections();
|
|
51
|
+
if (!data.connections[name]) {
|
|
52
|
+
throw new Error(`Connection '${name}' not found`);
|
|
53
|
+
}
|
|
54
|
+
data.connections[name] = {
|
|
55
|
+
...data.connections[name],
|
|
56
|
+
...config,
|
|
57
|
+
};
|
|
58
|
+
await this.saveConnections(data);
|
|
59
|
+
}
|
|
60
|
+
async removeConnection(name) {
|
|
61
|
+
const data = await this.loadConnections();
|
|
62
|
+
if (!data.connections[name]) {
|
|
63
|
+
throw new Error(`Connection '${name}' not found`);
|
|
64
|
+
}
|
|
65
|
+
delete data.connections[name];
|
|
66
|
+
await this.saveConnections(data);
|
|
67
|
+
}
|
|
68
|
+
async getConnection(name) {
|
|
69
|
+
const data = await this.loadConnections();
|
|
70
|
+
if (!data.connections[name]) {
|
|
71
|
+
throw new Error(`Connection '${name}' not found`);
|
|
72
|
+
}
|
|
73
|
+
return data.connections[name];
|
|
74
|
+
}
|
|
75
|
+
async listConnections() {
|
|
76
|
+
const data = await this.loadConnections();
|
|
77
|
+
const result = {};
|
|
78
|
+
for (const [name, config] of Object.entries(data.connections)) {
|
|
79
|
+
const { password, ...safeConfig } = config;
|
|
80
|
+
result[name] = safeConfig;
|
|
81
|
+
}
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
class PostgresDatabase {
|
|
86
|
+
client = null;
|
|
87
|
+
config;
|
|
88
|
+
constructor(config) {
|
|
89
|
+
this.config = config;
|
|
90
|
+
}
|
|
91
|
+
async connect() {
|
|
92
|
+
this.client = new Client({
|
|
93
|
+
host: this.config.host,
|
|
94
|
+
port: this.config.port,
|
|
95
|
+
database: this.config.database,
|
|
96
|
+
user: this.config.user,
|
|
97
|
+
password: this.config.password,
|
|
98
|
+
ssl: this.config.ssl ? { rejectUnauthorized: false } : false,
|
|
99
|
+
});
|
|
100
|
+
await this.client.connect();
|
|
101
|
+
}
|
|
102
|
+
async query(sql) {
|
|
103
|
+
if (!this.client) {
|
|
104
|
+
throw new Error("Not connected to database");
|
|
105
|
+
}
|
|
106
|
+
const result = await this.client.query(sql);
|
|
107
|
+
return result.rows;
|
|
108
|
+
}
|
|
109
|
+
async disconnect() {
|
|
110
|
+
if (this.client) {
|
|
111
|
+
await this.client.end();
|
|
112
|
+
this.client = null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
isConnected() {
|
|
116
|
+
return this.client !== null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
async function main() {
|
|
120
|
+
const connectionManager = new ConnectionManager();
|
|
121
|
+
let database = null;
|
|
122
|
+
let currentConnectionName = null;
|
|
123
|
+
const server = new McpServer({
|
|
124
|
+
name: "mcp-postgres-sql",
|
|
125
|
+
description: "PostgreSQL query server for MCP with connection management",
|
|
126
|
+
version: "1.0.0",
|
|
127
|
+
});
|
|
128
|
+
// Tool: add_connection
|
|
129
|
+
server.tool("add_connection", "Add a new PostgreSQL connection configuration", {
|
|
130
|
+
name: z.string().describe("Name for this connection"),
|
|
131
|
+
host: z.string().describe("Database host"),
|
|
132
|
+
port: z.number().default(5432).describe("Database port"),
|
|
133
|
+
database: z.string().describe("Database name"),
|
|
134
|
+
user: z.string().describe("Database user"),
|
|
135
|
+
password: z.string().describe("Database password"),
|
|
136
|
+
ssl: z.boolean().default(false).describe("Use SSL connection"),
|
|
137
|
+
}, async ({ name, host, port, database, user, password, ssl }) => {
|
|
138
|
+
try {
|
|
139
|
+
await connectionManager.addConnection(name, {
|
|
140
|
+
host,
|
|
141
|
+
port,
|
|
142
|
+
database,
|
|
143
|
+
user,
|
|
144
|
+
password,
|
|
145
|
+
ssl,
|
|
146
|
+
});
|
|
147
|
+
return {
|
|
148
|
+
content: [
|
|
149
|
+
{
|
|
150
|
+
type: "text",
|
|
151
|
+
text: `Connection '${name}' added successfully`,
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
return {
|
|
158
|
+
isError: true,
|
|
159
|
+
content: [
|
|
160
|
+
{
|
|
161
|
+
type: "text",
|
|
162
|
+
text: `Error adding connection: ${error instanceof Error ? error.message : String(error)}`,
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
// Tool: list_connections
|
|
169
|
+
server.tool("list_connections", "List all saved PostgreSQL connections (passwords hidden)", {}, async () => {
|
|
170
|
+
try {
|
|
171
|
+
const connections = await connectionManager.listConnections();
|
|
172
|
+
return {
|
|
173
|
+
content: [
|
|
174
|
+
{
|
|
175
|
+
type: "text",
|
|
176
|
+
text: JSON.stringify(connections, null, 2),
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
return {
|
|
183
|
+
isError: true,
|
|
184
|
+
content: [
|
|
185
|
+
{
|
|
186
|
+
type: "text",
|
|
187
|
+
text: `Error listing connections: ${error instanceof Error ? error.message : String(error)}`,
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
// Tool: update_connection
|
|
194
|
+
server.tool("update_connection", "Update an existing PostgreSQL connection configuration", {
|
|
195
|
+
name: z.string().describe("Name of the connection to update"),
|
|
196
|
+
host: z.string().optional().describe("Database host"),
|
|
197
|
+
port: z.number().optional().describe("Database port"),
|
|
198
|
+
database: z.string().optional().describe("Database name"),
|
|
199
|
+
user: z.string().optional().describe("Database user"),
|
|
200
|
+
password: z.string().optional().describe("Database password"),
|
|
201
|
+
ssl: z.boolean().optional().describe("Use SSL connection"),
|
|
202
|
+
}, async ({ name, host, port, database, user, password, ssl }) => {
|
|
203
|
+
try {
|
|
204
|
+
const updates = {};
|
|
205
|
+
if (host !== undefined)
|
|
206
|
+
updates.host = host;
|
|
207
|
+
if (port !== undefined)
|
|
208
|
+
updates.port = port;
|
|
209
|
+
if (database !== undefined)
|
|
210
|
+
updates.database = database;
|
|
211
|
+
if (user !== undefined)
|
|
212
|
+
updates.user = user;
|
|
213
|
+
if (password !== undefined)
|
|
214
|
+
updates.password = password;
|
|
215
|
+
if (ssl !== undefined)
|
|
216
|
+
updates.ssl = ssl;
|
|
217
|
+
await connectionManager.updateConnection(name, updates);
|
|
218
|
+
return {
|
|
219
|
+
content: [
|
|
220
|
+
{
|
|
221
|
+
type: "text",
|
|
222
|
+
text: `Connection '${name}' updated successfully`,
|
|
223
|
+
},
|
|
224
|
+
],
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
catch (error) {
|
|
228
|
+
return {
|
|
229
|
+
isError: true,
|
|
230
|
+
content: [
|
|
231
|
+
{
|
|
232
|
+
type: "text",
|
|
233
|
+
text: `Error updating connection: ${error instanceof Error ? error.message : String(error)}`,
|
|
234
|
+
},
|
|
235
|
+
],
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
// Tool: remove_connection
|
|
240
|
+
server.tool("remove_connection", "Remove a saved PostgreSQL connection", {
|
|
241
|
+
name: z.string().describe("Name of the connection to remove"),
|
|
242
|
+
}, async ({ name }) => {
|
|
243
|
+
try {
|
|
244
|
+
// Disconnect if this is the current connection
|
|
245
|
+
if (currentConnectionName === name && database) {
|
|
246
|
+
await database.disconnect();
|
|
247
|
+
database = null;
|
|
248
|
+
currentConnectionName = null;
|
|
249
|
+
}
|
|
250
|
+
await connectionManager.removeConnection(name);
|
|
251
|
+
return {
|
|
252
|
+
content: [
|
|
253
|
+
{
|
|
254
|
+
type: "text",
|
|
255
|
+
text: `Connection '${name}' removed successfully`,
|
|
256
|
+
},
|
|
257
|
+
],
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
catch (error) {
|
|
261
|
+
return {
|
|
262
|
+
isError: true,
|
|
263
|
+
content: [
|
|
264
|
+
{
|
|
265
|
+
type: "text",
|
|
266
|
+
text: `Error removing connection: ${error instanceof Error ? error.message : String(error)}`,
|
|
267
|
+
},
|
|
268
|
+
],
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
// Tool: connect
|
|
273
|
+
server.tool("connect", "Connect to a saved PostgreSQL database connection", {
|
|
274
|
+
name: z.string().describe("Name of the connection to use"),
|
|
275
|
+
}, async ({ name }) => {
|
|
276
|
+
try {
|
|
277
|
+
// Disconnect from current connection if any
|
|
278
|
+
if (database) {
|
|
279
|
+
await database.disconnect();
|
|
280
|
+
database = null;
|
|
281
|
+
currentConnectionName = null;
|
|
282
|
+
}
|
|
283
|
+
const config = await connectionManager.getConnection(name);
|
|
284
|
+
database = new PostgresDatabase(config);
|
|
285
|
+
await database.connect();
|
|
286
|
+
currentConnectionName = name;
|
|
287
|
+
return {
|
|
288
|
+
content: [
|
|
289
|
+
{
|
|
290
|
+
type: "text",
|
|
291
|
+
text: `Connected to '${name}' at ${config.host}:${config.port}/${config.database}`,
|
|
292
|
+
},
|
|
293
|
+
],
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
catch (error) {
|
|
297
|
+
database = null;
|
|
298
|
+
currentConnectionName = null;
|
|
299
|
+
return {
|
|
300
|
+
isError: true,
|
|
301
|
+
content: [
|
|
302
|
+
{
|
|
303
|
+
type: "text",
|
|
304
|
+
text: `Error connecting to database: ${error instanceof Error ? error.message : String(error)}`,
|
|
305
|
+
},
|
|
306
|
+
],
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
// Tool: disconnect
|
|
311
|
+
server.tool("disconnect", "Disconnect from the current PostgreSQL database", {}, async () => {
|
|
312
|
+
try {
|
|
313
|
+
if (!database) {
|
|
314
|
+
return {
|
|
315
|
+
content: [
|
|
316
|
+
{
|
|
317
|
+
type: "text",
|
|
318
|
+
text: "No active database connection",
|
|
319
|
+
},
|
|
320
|
+
],
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
const connectionName = currentConnectionName;
|
|
324
|
+
await database.disconnect();
|
|
325
|
+
database = null;
|
|
326
|
+
currentConnectionName = null;
|
|
327
|
+
return {
|
|
328
|
+
content: [
|
|
329
|
+
{
|
|
330
|
+
type: "text",
|
|
331
|
+
text: `Disconnected from '${connectionName}'`,
|
|
332
|
+
},
|
|
333
|
+
],
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
catch (error) {
|
|
337
|
+
return {
|
|
338
|
+
isError: true,
|
|
339
|
+
content: [
|
|
340
|
+
{
|
|
341
|
+
type: "text",
|
|
342
|
+
text: `Error disconnecting: ${error instanceof Error ? error.message : String(error)}`,
|
|
343
|
+
},
|
|
344
|
+
],
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
// Tool: query
|
|
349
|
+
server.tool("query", "Execute a SQL query against the connected PostgreSQL database", {
|
|
350
|
+
sql: z.string().describe("The SQL query to execute"),
|
|
351
|
+
}, async ({ sql }) => {
|
|
352
|
+
try {
|
|
353
|
+
if (!database || !database.isConnected()) {
|
|
354
|
+
throw new Error("Not connected to any database. Use 'connect' tool first.");
|
|
355
|
+
}
|
|
356
|
+
const results = await database.query(sql);
|
|
357
|
+
return {
|
|
358
|
+
content: [
|
|
359
|
+
{
|
|
360
|
+
type: "text",
|
|
361
|
+
text: JSON.stringify(results, null, 2),
|
|
362
|
+
},
|
|
363
|
+
],
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
catch (error) {
|
|
367
|
+
return {
|
|
368
|
+
isError: true,
|
|
369
|
+
content: [
|
|
370
|
+
{
|
|
371
|
+
type: "text",
|
|
372
|
+
text: `Error executing query: ${error instanceof Error ? error.message : String(error)}`,
|
|
373
|
+
},
|
|
374
|
+
],
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
const transport = new StdioServerTransport();
|
|
379
|
+
await server.connect(transport);
|
|
380
|
+
console.error("MCP PostgreSQL Server running on stdio");
|
|
381
|
+
// Graceful shutdown handlers
|
|
382
|
+
const shutdown = async () => {
|
|
383
|
+
if (database) {
|
|
384
|
+
await database.disconnect();
|
|
385
|
+
}
|
|
386
|
+
process.exit(0);
|
|
387
|
+
};
|
|
388
|
+
process.on("SIGINT", shutdown);
|
|
389
|
+
process.on("SIGTERM", shutdown);
|
|
390
|
+
}
|
|
391
|
+
main().catch((error) => {
|
|
392
|
+
console.error("Fatal error:", error);
|
|
393
|
+
process.exit(1);
|
|
394
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@spec-this/mcp-postgres-sql",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for PostgreSQL database queries",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"mcp-postgres-sql": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc && chmod 755 dist/index.js",
|
|
15
|
+
"start": "node dist/index.js",
|
|
16
|
+
"dev": "tsx src/index.ts",
|
|
17
|
+
"prepublishOnly": "npm run build",
|
|
18
|
+
"typecheck": "tsc --noEmit",
|
|
19
|
+
"test:up": "docker compose up -d",
|
|
20
|
+
"test:down": "docker compose down -v",
|
|
21
|
+
"test": "tsx --test test/*.test.ts",
|
|
22
|
+
"test:ci": "docker compose up -d && sleep 10 && npm test && docker compose down -v",
|
|
23
|
+
"semantic-release": "semantic-release"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"modelcontextprotocol",
|
|
27
|
+
"mcp",
|
|
28
|
+
"server",
|
|
29
|
+
"postgresql",
|
|
30
|
+
"postgres",
|
|
31
|
+
"database"
|
|
32
|
+
],
|
|
33
|
+
"author": "",
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"files": [
|
|
36
|
+
"dist"
|
|
37
|
+
],
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
40
|
+
"zod": "^3.24.2",
|
|
41
|
+
"pg": "^8.13.1"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@semantic-release/changelog": "^6.0.3",
|
|
45
|
+
"@semantic-release/git": "^10.0.1",
|
|
46
|
+
"@types/node": "^22.13.14",
|
|
47
|
+
"@types/pg": "^8.11.10",
|
|
48
|
+
"semantic-release": "^24.0.0",
|
|
49
|
+
"tsx": "^4.17.0",
|
|
50
|
+
"typescript": "^5.8.2"
|
|
51
|
+
},
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=18"
|
|
54
|
+
}
|
|
55
|
+
}
|