mirror of
https://github.com/executeautomation/mcp-database-server.git
synced 2025-12-09 21:12:57 +08:00
Add PostgreSQL support to MCP Database Server
Updated package.json and package-lock.json to include PostgreSQL dependencies. Enhanced README with PostgreSQL usage instructions and configuration details. Modified index.ts to handle PostgreSQL connection parameters and logging. Added PostgresqlAdapter for database interactions.
This commit is contained in:
107
docs/postgresql-setup.md
Normal file
107
docs/postgresql-setup.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# PostgreSQL Setup for MCP Database Server
|
||||
|
||||
This document describes how to set up and use the PostgreSQL adapter with the MCP Database Server.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. You need to have PostgreSQL installed and running on your system or on a remote server.
|
||||
2. Ensure the pg package is installed:
|
||||
|
||||
```
|
||||
npm install pg
|
||||
npm install @types/pg --save-dev
|
||||
```
|
||||
|
||||
## Running the Server with PostgreSQL
|
||||
|
||||
To connect to a PostgreSQL database, use the following command-line arguments:
|
||||
|
||||
```bash
|
||||
# Basic connection
|
||||
node dist/src/index.js --postgresql --host localhost --database yourdb --user postgres --password yourpassword
|
||||
|
||||
# With custom port (default is 5432)
|
||||
node dist/src/index.js --postgresql --host localhost --database yourdb --user postgres --password yourpassword --port 5433
|
||||
|
||||
# With SSL enabled
|
||||
node dist/src/index.js --postgresql --host localhost --database yourdb --user postgres --password yourpassword --ssl true
|
||||
```
|
||||
|
||||
## Command Line Arguments
|
||||
|
||||
- `--postgresql` or `--postgres`: Specifies that you want to connect to a PostgreSQL database.
|
||||
- `--host`: The hostname or IP address of the PostgreSQL server (required).
|
||||
- `--database`: The name of the database to connect to (required).
|
||||
- `--user`: The PostgreSQL user to authenticate as.
|
||||
- `--password`: The password for the PostgreSQL user.
|
||||
- `--port`: The port the PostgreSQL server is listening on (default: 5432).
|
||||
- `--ssl`: Whether to use SSL for the connection (true/false).
|
||||
|
||||
## Usage from MCP Client
|
||||
|
||||
The MCP client can interact with a PostgreSQL database using the same tools that are available for SQLite and SQL Server. The server automatically translates the generic SQL queries to PostgreSQL-specific formats.
|
||||
|
||||
## Supported Features
|
||||
|
||||
- Full SQL query support for SELECT, INSERT, UPDATE, and DELETE operations.
|
||||
- Table management (CREATE TABLE, ALTER TABLE, DROP TABLE).
|
||||
- Schema introspection.
|
||||
- Connection pooling for efficient database access.
|
||||
- SSL support for secure connections.
|
||||
|
||||
## Examples
|
||||
|
||||
### Create a Table
|
||||
|
||||
```sql
|
||||
CREATE TABLE users (
|
||||
id SERIAL PRIMARY KEY,
|
||||
username VARCHAR(50) NOT NULL,
|
||||
email VARCHAR(100) UNIQUE NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
```
|
||||
|
||||
### Insert Data
|
||||
|
||||
```sql
|
||||
INSERT INTO users (username, email) VALUES ('johndoe', 'john@example.com');
|
||||
```
|
||||
|
||||
### Query Data
|
||||
|
||||
```sql
|
||||
SELECT * FROM users WHERE username = 'johndoe';
|
||||
```
|
||||
|
||||
## Limitations
|
||||
|
||||
- For the `run` method with INSERT statements, the adapter attempts to retrieve the last inserted ID by adding a RETURNING clause. This assumes your tables have an 'id' column.
|
||||
- Complex stored procedures or PostgreSQL-specific features might require custom implementation.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Connection Issues
|
||||
|
||||
If you're having trouble connecting to your PostgreSQL database:
|
||||
|
||||
1. Verify that PostgreSQL is running: `pg_isready -h localhost -p 5432`
|
||||
2. Check that your credentials are correct.
|
||||
3. Ensure that the database exists and the user has appropriate permissions.
|
||||
4. Check firewall settings if connecting to a remote database.
|
||||
|
||||
### Query Errors
|
||||
|
||||
If your queries are failing:
|
||||
|
||||
1. Check the syntax against PostgreSQL's SQL dialect.
|
||||
2. Verify table and column names.
|
||||
3. Check that the user has proper permissions for the operations.
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
For optimal performance:
|
||||
|
||||
1. Use parameterized queries to prevent SQL injection and improve query caching.
|
||||
2. Consider indexing frequently queried columns.
|
||||
3. For large result sets, use LIMIT and OFFSET for pagination.
|
||||
238
package-lock.json
generated
238
package-lock.json
generated
@@ -11,6 +11,7 @@
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "1.9.0",
|
||||
"mssql": "11.0.1",
|
||||
"pg": "^8.11.3",
|
||||
"sqlite3": "5.1.7"
|
||||
},
|
||||
"bin": {
|
||||
@@ -18,6 +19,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mssql": "^9.1.5",
|
||||
"@types/pg": "^8.11.13",
|
||||
"@types/sqlite3": "5.1.0",
|
||||
"rimraf": "^5.0.5",
|
||||
"shx": "0.4.0",
|
||||
@@ -492,6 +494,17 @@
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/pg": {
|
||||
"version": "8.11.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.13.tgz",
|
||||
"integrity": "sha512-6kXByGkvRvwXLuyaWzsebs2du6+XuAB2CuMsuzP7uaihQahshVgSmB22Pmh0vQMkQ1h5+PZU0d+Di1o+WpVWJg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"pg-protocol": "*",
|
||||
"pg-types": "^4.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/readable-stream": {
|
||||
"version": "4.0.18",
|
||||
"resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.18.tgz",
|
||||
@@ -737,6 +750,14 @@
|
||||
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
|
||||
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
|
||||
},
|
||||
"node_modules/buffer-writer": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz",
|
||||
"integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/bundle-name": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz",
|
||||
@@ -2718,6 +2739,12 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/obuf": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
|
||||
"integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/on-finished": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||
@@ -2784,6 +2811,11 @@
|
||||
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/packet-reader": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz",
|
||||
"integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ=="
|
||||
},
|
||||
"node_modules/parseurl": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||
@@ -2839,6 +2871,151 @@
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/pg": {
|
||||
"version": "8.11.3",
|
||||
"resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz",
|
||||
"integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==",
|
||||
"dependencies": {
|
||||
"buffer-writer": "2.0.0",
|
||||
"packet-reader": "1.0.0",
|
||||
"pg-connection-string": "^2.6.2",
|
||||
"pg-pool": "^3.6.1",
|
||||
"pg-protocol": "^1.6.0",
|
||||
"pg-types": "^2.1.0",
|
||||
"pgpass": "1.x"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"pg-cloudflare": "^1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"pg-native": ">=3.0.1"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"pg-native": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/pg-cloudflare": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz",
|
||||
"integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/pg-connection-string": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.7.0.tgz",
|
||||
"integrity": "sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA=="
|
||||
},
|
||||
"node_modules/pg-int8": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
|
||||
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pg-numeric": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/pg-numeric/-/pg-numeric-1.0.2.tgz",
|
||||
"integrity": "sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/pg-pool": {
|
||||
"version": "3.8.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.8.0.tgz",
|
||||
"integrity": "sha512-VBw3jiVm6ZOdLBTIcXLNdSotb6Iy3uOCwDGFAksZCXmi10nyRvnP2v3jl4d+IsLYRyXf6o9hIm/ZtUzlByNUdw==",
|
||||
"peerDependencies": {
|
||||
"pg": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pg-protocol": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.8.0.tgz",
|
||||
"integrity": "sha512-jvuYlEkL03NRvOoyoRktBK7+qU5kOvlAwvmrH8sr3wbLrOdVWsRxQfz8mMy9sZFsqJ1hEWNfdWKI4SAmoL+j7g=="
|
||||
},
|
||||
"node_modules/pg-types": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-4.0.2.tgz",
|
||||
"integrity": "sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"pg-int8": "1.0.1",
|
||||
"pg-numeric": "1.0.2",
|
||||
"postgres-array": "~3.0.1",
|
||||
"postgres-bytea": "~3.0.0",
|
||||
"postgres-date": "~2.1.0",
|
||||
"postgres-interval": "^3.0.0",
|
||||
"postgres-range": "^1.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/pg/node_modules/pg-types": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
|
||||
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
|
||||
"dependencies": {
|
||||
"pg-int8": "1.0.1",
|
||||
"postgres-array": "~2.0.0",
|
||||
"postgres-bytea": "~1.0.0",
|
||||
"postgres-date": "~1.0.4",
|
||||
"postgres-interval": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/pg/node_modules/postgres-array": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
|
||||
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/pg/node_modules/postgres-bytea": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
|
||||
"integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pg/node_modules/postgres-date": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
|
||||
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pg/node_modules/postgres-interval": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
|
||||
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
|
||||
"dependencies": {
|
||||
"xtend": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pgpass": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
|
||||
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
|
||||
"dependencies": {
|
||||
"split2": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
@@ -2859,6 +3036,51 @@
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-array": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.4.tgz",
|
||||
"integrity": "sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-bytea": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz",
|
||||
"integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"obuf": "~1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-date": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-2.1.0.tgz",
|
||||
"integrity": "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-interval": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz",
|
||||
"integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-range": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/postgres-range/-/postgres-range-1.1.4.tgz",
|
||||
"integrity": "sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/prebuild-install": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz",
|
||||
@@ -3430,6 +3652,14 @@
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/split2": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
|
||||
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
|
||||
"engines": {
|
||||
"node": ">= 10.x"
|
||||
}
|
||||
},
|
||||
"node_modules/sprintf-js": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
|
||||
@@ -4038,6 +4268,14 @@
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
||||
},
|
||||
"node_modules/xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
|
||||
"engines": {
|
||||
"node": ">=0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
|
||||
@@ -25,10 +25,12 @@
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "1.9.0",
|
||||
"mssql": "11.0.1",
|
||||
"pg": "^8.11.3",
|
||||
"sqlite3": "5.1.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mssql": "^9.1.5",
|
||||
"@types/pg": "^8.11.13",
|
||||
"@types/sqlite3": "5.1.0",
|
||||
"rimraf": "^5.0.5",
|
||||
"shx": "0.4.0",
|
||||
|
||||
45
readme.md
45
readme.md
@@ -1,6 +1,6 @@
|
||||
# MCP Database Server
|
||||
|
||||
This MCP (Model Context Protocol) server provides database access capabilities to Claude, supporting both SQLite and SQL Server databases.
|
||||
This MCP (Model Context Protocol) server provides database access capabilities to Claude, supporting SQLite, SQL Server, and PostgreSQL databases.
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -71,6 +71,24 @@ Optional parameters:
|
||||
- `--password`: Password for SQL Server authentication
|
||||
- `--port`: Port number (default: 1433)
|
||||
|
||||
### PostgreSQL Database
|
||||
|
||||
To use with a PostgreSQL database:
|
||||
|
||||
```
|
||||
node dist/src/index.js --postgresql --host <host-name> --database <database-name> [--user <username> --password <password>]
|
||||
```
|
||||
|
||||
Required parameters:
|
||||
- `--host`: PostgreSQL host name or IP address
|
||||
- `--database`: Name of the database
|
||||
|
||||
Optional parameters:
|
||||
- `--user`: Username for PostgreSQL authentication
|
||||
- `--password`: Password for PostgreSQL authentication
|
||||
- `--port`: Port number (default: 5432)
|
||||
- `--ssl`: Enable SSL connection (true/false)
|
||||
|
||||
## Configuring Claude Desktop
|
||||
|
||||
### Direct Usage Configuration
|
||||
@@ -99,6 +117,18 @@ If you installed the package globally, configure Claude Desktop with:
|
||||
"--user", "your-username",
|
||||
"--password", "your-password"
|
||||
]
|
||||
},
|
||||
"postgresql": {
|
||||
"command": "npx",
|
||||
"args": [
|
||||
"-y",
|
||||
"@executeautomation/database-server",
|
||||
"--postgresql",
|
||||
"--host", "your-host-name",
|
||||
"--database", "your-database-name",
|
||||
"--user", "your-username",
|
||||
"--password", "your-password"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -128,6 +158,17 @@ For local development, configure Claude Desktop to use your locally built versio
|
||||
"--user", "your-username",
|
||||
"--password", "your-password"
|
||||
]
|
||||
},
|
||||
"postgresql": {
|
||||
"command": "node",
|
||||
"args": [
|
||||
"/absolute/path/to/mcp-database-server/dist/src/index.js",
|
||||
"--postgresql",
|
||||
"--host", "your-host-name",
|
||||
"--database", "your-database-name",
|
||||
"--user", "your-username",
|
||||
"--password", "your-password"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -160,6 +201,7 @@ For practical examples of how to use these tools with Claude, see [Usage Example
|
||||
## Additional Documentation
|
||||
|
||||
- [SQL Server Setup Guide](docs/sql-server-setup.md): Details on connecting to SQL Server databases
|
||||
- [PostgreSQL Setup Guide](docs/postgresql-setup.md): Details on connecting to PostgreSQL databases
|
||||
- [Usage Examples](docs/usage-examples.md): Example queries and commands to use with Claude
|
||||
|
||||
## Development
|
||||
@@ -180,6 +222,7 @@ npm run watch
|
||||
|
||||
- Node.js 18+
|
||||
- For SQL Server connectivity: SQL Server 2012 or later
|
||||
- For PostgreSQL connectivity: PostgreSQL 9.5 or later
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ export interface DbAdapter {
|
||||
// Import adapters using dynamic imports
|
||||
import { SqliteAdapter } from './sqlite-adapter.js';
|
||||
import { SqlServerAdapter } from './sqlserver-adapter.js';
|
||||
import { PostgresqlAdapter } from './postgresql-adapter.js';
|
||||
|
||||
/**
|
||||
* Factory function to create the appropriate database adapter
|
||||
@@ -68,6 +69,9 @@ export function createDbAdapter(type: string, connectionInfo: any): DbAdapter {
|
||||
}
|
||||
case 'sqlserver':
|
||||
return new SqlServerAdapter(connectionInfo);
|
||||
case 'postgresql':
|
||||
case 'postgres':
|
||||
return new PostgresqlAdapter(connectionInfo);
|
||||
default:
|
||||
throw new Error(`Unsupported database type: ${type}`);
|
||||
}
|
||||
|
||||
185
src/db/postgresql-adapter.ts
Normal file
185
src/db/postgresql-adapter.ts
Normal file
@@ -0,0 +1,185 @@
|
||||
import { DbAdapter } from "./adapter.js";
|
||||
import pg from 'pg';
|
||||
|
||||
/**
|
||||
* PostgreSQL database adapter implementation
|
||||
*/
|
||||
export class PostgresqlAdapter implements DbAdapter {
|
||||
private client: pg.Client | null = null;
|
||||
private config: pg.ClientConfig;
|
||||
private host: string;
|
||||
private database: string;
|
||||
|
||||
constructor(connectionInfo: {
|
||||
host: string;
|
||||
database: string;
|
||||
user?: string;
|
||||
password?: string;
|
||||
port?: number;
|
||||
ssl?: boolean | object;
|
||||
options?: any;
|
||||
}) {
|
||||
this.host = connectionInfo.host;
|
||||
this.database = connectionInfo.database;
|
||||
|
||||
// Create PostgreSQL connection config
|
||||
this.config = {
|
||||
host: connectionInfo.host,
|
||||
database: connectionInfo.database,
|
||||
port: connectionInfo.port || 5432,
|
||||
user: connectionInfo.user,
|
||||
password: connectionInfo.password,
|
||||
ssl: connectionInfo.ssl
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize PostgreSQL connection
|
||||
*/
|
||||
async init(): Promise<void> {
|
||||
try {
|
||||
console.error(`[INFO] Connecting to PostgreSQL: ${this.host}, Database: ${this.database}`);
|
||||
this.client = new pg.Client(this.config);
|
||||
await this.client.connect();
|
||||
console.error(`[INFO] PostgreSQL connection established successfully`);
|
||||
} catch (err) {
|
||||
console.error(`[ERROR] PostgreSQL connection error: ${(err as Error).message}`);
|
||||
throw new Error(`Failed to connect to PostgreSQL: ${(err as Error).message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a SQL query and get all results
|
||||
* @param query SQL query to execute
|
||||
* @param params Query parameters
|
||||
* @returns Promise with query results
|
||||
*/
|
||||
async all(query: string, params: any[] = []): Promise<any[]> {
|
||||
if (!this.client) {
|
||||
throw new Error("Database not initialized");
|
||||
}
|
||||
|
||||
try {
|
||||
// PostgreSQL uses $1, $2, etc. for parameterized queries
|
||||
const preparedQuery = query.replace(/\?/g, (_, i) => `$${i + 1}`);
|
||||
|
||||
const result = await this.client.query(preparedQuery, params);
|
||||
return result.rows;
|
||||
} catch (err) {
|
||||
throw new Error(`PostgreSQL query error: ${(err as Error).message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a SQL query that modifies data
|
||||
* @param query SQL query to execute
|
||||
* @param params Query parameters
|
||||
* @returns Promise with result info
|
||||
*/
|
||||
async run(query: string, params: any[] = []): Promise<{ changes: number, lastID: number }> {
|
||||
if (!this.client) {
|
||||
throw new Error("Database not initialized");
|
||||
}
|
||||
|
||||
try {
|
||||
// Replace ? with numbered parameters
|
||||
const preparedQuery = query.replace(/\?/g, (_, i) => `$${i + 1}`);
|
||||
|
||||
let lastID = 0;
|
||||
let changes = 0;
|
||||
|
||||
// For INSERT queries, try to get the inserted ID
|
||||
if (query.trim().toUpperCase().startsWith('INSERT')) {
|
||||
// Add RETURNING clause to get the inserted ID if it doesn't already have one
|
||||
const returningQuery = preparedQuery.includes('RETURNING')
|
||||
? preparedQuery
|
||||
: `${preparedQuery} RETURNING id`;
|
||||
|
||||
const result = await this.client.query(returningQuery, params);
|
||||
changes = result.rowCount || 0;
|
||||
lastID = result.rows[0]?.id || 0;
|
||||
} else {
|
||||
const result = await this.client.query(preparedQuery, params);
|
||||
changes = result.rowCount || 0;
|
||||
}
|
||||
|
||||
return { changes, lastID };
|
||||
} catch (err) {
|
||||
throw new Error(`PostgreSQL query error: ${(err as Error).message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute multiple SQL statements
|
||||
* @param query SQL statements to execute
|
||||
* @returns Promise that resolves when execution completes
|
||||
*/
|
||||
async exec(query: string): Promise<void> {
|
||||
if (!this.client) {
|
||||
throw new Error("Database not initialized");
|
||||
}
|
||||
|
||||
try {
|
||||
await this.client.query(query);
|
||||
} catch (err) {
|
||||
throw new Error(`PostgreSQL batch error: ${(err as Error).message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the database connection
|
||||
*/
|
||||
async close(): Promise<void> {
|
||||
if (this.client) {
|
||||
await this.client.end();
|
||||
this.client = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get database metadata
|
||||
*/
|
||||
getMetadata(): { name: string, type: string, server: string, database: string } {
|
||||
return {
|
||||
name: "PostgreSQL",
|
||||
type: "postgresql",
|
||||
server: this.host,
|
||||
database: this.database
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get database-specific query for listing tables
|
||||
*/
|
||||
getListTablesQuery(): string {
|
||||
return "SELECT table_name as name FROM information_schema.tables WHERE table_schema = 'public' ORDER BY table_name";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get database-specific query for describing a table
|
||||
* @param tableName Table name
|
||||
*/
|
||||
getDescribeTableQuery(tableName: string): string {
|
||||
return `
|
||||
SELECT
|
||||
c.column_name as name,
|
||||
c.data_type as type,
|
||||
CASE WHEN c.is_nullable = 'NO' THEN 1 ELSE 0 END as notnull,
|
||||
CASE WHEN pk.constraint_name IS NOT NULL THEN 1 ELSE 0 END as pk,
|
||||
c.column_default as dflt_value
|
||||
FROM
|
||||
information_schema.columns c
|
||||
LEFT JOIN
|
||||
information_schema.key_column_usage kcu
|
||||
ON c.table_name = kcu.table_name AND c.column_name = kcu.column_name
|
||||
LEFT JOIN
|
||||
information_schema.table_constraints pk
|
||||
ON kcu.constraint_name = pk.constraint_name AND pk.constraint_type = 'PRIMARY KEY'
|
||||
WHERE
|
||||
c.table_name = '${tableName}'
|
||||
AND c.table_schema = 'public'
|
||||
ORDER BY
|
||||
c.ordinal_position
|
||||
`;
|
||||
}
|
||||
}
|
||||
38
src/index.ts
38
src/index.ts
@@ -44,6 +44,7 @@ if (args.length === 0) {
|
||||
logger.error("Please provide database connection information");
|
||||
logger.error("Usage for SQLite: node index.js <database_file_path>");
|
||||
logger.error("Usage for SQL Server: node index.js --sqlserver --server <server> --database <database> [--user <user> --password <password>]");
|
||||
logger.error("Usage for PostgreSQL: node index.js --postgresql --host <host> --database <database> [--user <user> --password <password> --port <port>]");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@@ -81,6 +82,41 @@ if (args.includes('--sqlserver')) {
|
||||
logger.error("Error: SQL Server requires --server and --database parameters");
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
// Check if using PostgreSQL
|
||||
else if (args.includes('--postgresql') || args.includes('--postgres')) {
|
||||
dbType = 'postgresql';
|
||||
connectionInfo = {
|
||||
host: '',
|
||||
database: '',
|
||||
user: undefined,
|
||||
password: undefined,
|
||||
port: undefined,
|
||||
ssl: undefined
|
||||
};
|
||||
|
||||
// Parse PostgreSQL connection parameters
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (args[i] === '--host' && i + 1 < args.length) {
|
||||
connectionInfo.host = args[i + 1];
|
||||
} else if (args[i] === '--database' && i + 1 < args.length) {
|
||||
connectionInfo.database = args[i + 1];
|
||||
} else if (args[i] === '--user' && i + 1 < args.length) {
|
||||
connectionInfo.user = args[i + 1];
|
||||
} else if (args[i] === '--password' && i + 1 < args.length) {
|
||||
connectionInfo.password = args[i + 1];
|
||||
} else if (args[i] === '--port' && i + 1 < args.length) {
|
||||
connectionInfo.port = parseInt(args[i + 1], 10);
|
||||
} else if (args[i] === '--ssl' && i + 1 < args.length) {
|
||||
connectionInfo.ssl = args[i + 1] === 'true';
|
||||
}
|
||||
}
|
||||
|
||||
// Validate PostgreSQL connection info
|
||||
if (!connectionInfo.host || !connectionInfo.database) {
|
||||
logger.error("Error: PostgreSQL requires --host and --database parameters");
|
||||
process.exit(1);
|
||||
}
|
||||
} else {
|
||||
// SQLite mode (default)
|
||||
dbType = 'sqlite';
|
||||
@@ -137,6 +173,8 @@ async function runServer() {
|
||||
logger.info(`Database path: ${connectionInfo}`);
|
||||
} else if (dbType === 'sqlserver') {
|
||||
logger.info(`Server: ${connectionInfo.server}, Database: ${connectionInfo.database}`);
|
||||
} else if (dbType === 'postgresql') {
|
||||
logger.info(`Host: ${connectionInfo.host}, Database: ${connectionInfo.database}`);
|
||||
}
|
||||
|
||||
// Initialize the database
|
||||
|
||||
Reference in New Issue
Block a user