mirror of
https://github.com/executeautomation/mcp-database-server.git
synced 2025-12-09 21:12:57 +08:00
Merge pull request #2 from executeautomation/Added-Postgres-database-support
Add PostgreSQL support to MCP Database Server
This commit is contained in:
111
docs/postgresql-setup.md
Normal file
111
docs/postgresql-setup.md
Normal file
@@ -0,0 +1,111 @@
|
||||
# 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
|
||||
|
||||
# With custom connection timeout (in milliseconds)
|
||||
node dist/src/index.js --postgresql --host localhost --database yourdb --user postgres --password yourpassword --connection-timeout 60000
|
||||
```
|
||||
|
||||
## 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).
|
||||
- `--connection-timeout`: The connection timeout in milliseconds (default: 30000).
|
||||
|
||||
## 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",
|
||||
|
||||
46
readme.md
46
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,25 @@ 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)
|
||||
- `--connection-timeout`: Connection timeout in milliseconds (default: 30000)
|
||||
|
||||
## Configuring Claude Desktop
|
||||
|
||||
### Direct Usage Configuration
|
||||
@@ -99,6 +118,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 +159,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 +202,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 +223,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
|
||||
|
||||
|
||||
BIN
src/.DS_Store
vendored
BIN
src/.DS_Store
vendored
Binary file not shown.
@@ -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}`);
|
||||
}
|
||||
|
||||
197
src/db/postgresql-adapter.ts
Normal file
197
src/db/postgresql-adapter.ts
Normal file
@@ -0,0 +1,197 @@
|
||||
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;
|
||||
connectionTimeout?: number;
|
||||
}) {
|
||||
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,
|
||||
// Add connection timeout if provided (in milliseconds)
|
||||
connectionTimeoutMillis: connectionInfo.connectionTimeout || 30000,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize PostgreSQL connection
|
||||
*/
|
||||
async init(): Promise<void> {
|
||||
try {
|
||||
console.error(`[INFO] Connecting to PostgreSQL: ${this.host}, Database: ${this.database}`);
|
||||
console.error(`[DEBUG] Connection details:`, {
|
||||
host: this.host,
|
||||
database: this.database,
|
||||
port: this.config.port,
|
||||
user: this.config.user,
|
||||
connectionTimeoutMillis: this.config.connectionTimeoutMillis,
|
||||
ssl: !!this.config.ssl
|
||||
});
|
||||
|
||||
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
|
||||
`;
|
||||
}
|
||||
}
|
||||
41
src/index.ts
41
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,44 @@ 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,
|
||||
connectionTimeout: 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';
|
||||
} else if (args[i] === '--connection-timeout' && i + 1 < args.length) {
|
||||
connectionInfo.connectionTimeout = parseInt(args[i + 1], 10);
|
||||
}
|
||||
}
|
||||
|
||||
// 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 +176,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