@kingsnow129/database-mcp 0.4.0 → 0.4.2
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 +13 -5
- package/dist/server.js +90 -18
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -13,11 +13,17 @@ It provides tools for:
|
|
|
13
13
|
## Release
|
|
14
14
|
|
|
15
15
|
Current release:
|
|
16
|
-
- NPM package: `@kingsnow129/database-mcp@0.4.
|
|
16
|
+
- NPM package: `@kingsnow129/database-mcp@0.4.2`
|
|
17
17
|
- MCP server name: `database-mcp`
|
|
18
|
-
- VSIX helper: `database-mcp-helper@0.4.
|
|
18
|
+
- VSIX helper: `database-mcp-helper@0.4.2`
|
|
19
19
|
|
|
20
|
-
## What Is New In 0.4.
|
|
20
|
+
## What Is New In 0.4.2
|
|
21
|
+
|
|
22
|
+
- Added SQL Server Windows integrated auth support with `integratedAuth`.
|
|
23
|
+
- Added `--integratedAuth` CLI override handling in `connect` flow.
|
|
24
|
+
- Added optional dependency `msnodesqlv8` for integrated auth SQL Server driver.
|
|
25
|
+
|
|
26
|
+
## What Was Introduced In 0.4.0
|
|
21
27
|
|
|
22
28
|
- Multi-database profile model (`servers` + `databases`) is now the primary config.
|
|
23
29
|
- Automatic profile resolution during `connect`:
|
|
@@ -111,14 +117,16 @@ Build and install locally:
|
|
|
111
117
|
cd vscode-extension
|
|
112
118
|
npm install
|
|
113
119
|
npm run package
|
|
114
|
-
code --install-extension database-mcp-helper-0.4.
|
|
120
|
+
code --install-extension database-mcp-helper-0.4.2.vsix --force
|
|
115
121
|
```
|
|
116
122
|
|
|
117
123
|
## Safety Defaults
|
|
118
124
|
|
|
119
|
-
- Read-only mode
|
|
125
|
+
- Read-only mode is hard-enforced (`connect.readOnly` overrides are ignored)
|
|
120
126
|
- Query tool only accepts one SELECT statement
|
|
121
127
|
- Semicolons are blocked
|
|
128
|
+
- Stored procedure execution (`exec`, `execute`, `sp_*`, `xp_*`) is blocked
|
|
129
|
+
- Function-call style execution patterns like `schema.fn(...)` are blocked
|
|
122
130
|
- Returned rows are capped (default `200`)
|
|
123
131
|
|
|
124
132
|
## Development
|
package/dist/server.js
CHANGED
|
@@ -17,9 +17,12 @@ dotenv.config();
|
|
|
17
17
|
|
|
18
18
|
const {Pool: PostgresPool}=pg;
|
|
19
19
|
|
|
20
|
+
let sqlMsnodesqlv8Cache;
|
|
21
|
+
|
|
20
22
|
const SUPPORTED_ENGINES=new Set(["sqlserver","postgres","mysql"]);
|
|
21
23
|
|
|
22
24
|
const CLI_OPTION_MAP={
|
|
25
|
+
target: "target",
|
|
23
26
|
alias: "alias",
|
|
24
27
|
serverName: "serverName",
|
|
25
28
|
defaultAlias: "defaultAlias",
|
|
@@ -34,6 +37,7 @@ const CLI_OPTION_MAP={
|
|
|
34
37
|
database: "database",
|
|
35
38
|
user: "user",
|
|
36
39
|
password: "password",
|
|
40
|
+
integratedAuth: "integratedAuth",
|
|
37
41
|
encrypt: "encrypt",
|
|
38
42
|
ssl: "ssl",
|
|
39
43
|
trustServerCertificate: "trustServerCertificate",
|
|
@@ -68,7 +72,7 @@ function parseCliArgs(argv) {
|
|
|
68
72
|
|
|
69
73
|
const rawToken=String(token).slice(2);
|
|
70
74
|
if(rawToken==="help") {
|
|
71
|
-
console.error("Supported flags: --alias --serverName --defaultAlias --profilesFile --currentServer --currentDatabase --engine --host --connectionString --server --port --database --user --password --encrypt --ssl --trustServerCertificate --readOnly --maxRows");
|
|
75
|
+
console.error("Supported flags: --target --alias --serverName --defaultAlias --profilesFile --currentServer --currentDatabase --engine --host --connectionString --server --port --database --user --password --integratedAuth --encrypt --ssl --trustServerCertificate --readOnly --maxRows");
|
|
72
76
|
process.exit(0);
|
|
73
77
|
}
|
|
74
78
|
|
|
@@ -194,6 +198,16 @@ function resolveSavedProfile(overrides={}) {
|
|
|
194
198
|
const profiles=loadProfiles();
|
|
195
199
|
const {servers,aliases}=profiles;
|
|
196
200
|
|
|
201
|
+
const requestedTarget=firstDefined(overrides.target,cliOptions.target);
|
|
202
|
+
const matchServerByHost=(hostOrServer) => {
|
|
203
|
+
const wanted=normalizeHostForCompare(hostOrServer);
|
|
204
|
+
return Object.entries(servers).find(([,cfg]) => {
|
|
205
|
+
const hostA=normalizeHostForCompare(cfg?.host);
|
|
206
|
+
const hostB=normalizeHostForCompare(cfg?.server);
|
|
207
|
+
return wanted===hostA||wanted===hostB;
|
|
208
|
+
});
|
|
209
|
+
};
|
|
210
|
+
|
|
197
211
|
const requestedAlias=firstDefined(overrides.alias,cliOptions.alias,process.env.DB_ALIAS);
|
|
198
212
|
const requestedServerName=firstDefined(
|
|
199
213
|
overrides.serverName,
|
|
@@ -209,6 +223,26 @@ function resolveSavedProfile(overrides={}) {
|
|
|
209
223
|
let serverName;
|
|
210
224
|
let serverProfile;
|
|
211
225
|
|
|
226
|
+
if(requestedTarget) {
|
|
227
|
+
const targetKey=String(requestedTarget).trim();
|
|
228
|
+
if(aliases[targetKey]&&typeof aliases[targetKey]==="object") {
|
|
229
|
+
source="target:alias";
|
|
230
|
+
aliasName=targetKey;
|
|
231
|
+
serverProfile=aliases[targetKey];
|
|
232
|
+
} else if(servers[targetKey]&&typeof servers[targetKey]==="object") {
|
|
233
|
+
source="target:serverName";
|
|
234
|
+
serverName=targetKey;
|
|
235
|
+
serverProfile=servers[targetKey];
|
|
236
|
+
} else {
|
|
237
|
+
const foundByTargetHost=matchServerByHost(targetKey);
|
|
238
|
+
if(foundByTargetHost) {
|
|
239
|
+
source="target:host";
|
|
240
|
+
serverName=foundByTargetHost[0];
|
|
241
|
+
serverProfile=foundByTargetHost[1];
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
212
246
|
if(requestedAlias) {
|
|
213
247
|
const aliasKey=String(requestedAlias).trim();
|
|
214
248
|
if(aliases[aliasKey]&&typeof aliases[aliasKey]==="object") {
|
|
@@ -232,12 +266,7 @@ function resolveSavedProfile(overrides={}) {
|
|
|
232
266
|
}
|
|
233
267
|
|
|
234
268
|
if(!serverProfile&&requestedHost) {
|
|
235
|
-
const
|
|
236
|
-
const found=Object.entries(servers).find(([,cfg]) => {
|
|
237
|
-
const hostA=normalizeHostForCompare(cfg?.host);
|
|
238
|
-
const hostB=normalizeHostForCompare(cfg?.server);
|
|
239
|
-
return wanted===hostA||wanted===hostB;
|
|
240
|
-
});
|
|
269
|
+
const found=matchServerByHost(requestedHost);
|
|
241
270
|
if(found) {
|
|
242
271
|
source="host";
|
|
243
272
|
serverName=found[0];
|
|
@@ -386,7 +415,12 @@ function buildConfig(overrides={}) {
|
|
|
386
415
|
);
|
|
387
416
|
|
|
388
417
|
const integratedAuth=boolFromEnv(
|
|
389
|
-
firstDefined(
|
|
418
|
+
firstDefined(
|
|
419
|
+
overrides.integratedAuth,
|
|
420
|
+
cliOptions.integratedAuth,
|
|
421
|
+
serverProfile?.integratedAuth,
|
|
422
|
+
process.env.DB_INTEGRATED_AUTH
|
|
423
|
+
),
|
|
390
424
|
false
|
|
391
425
|
);
|
|
392
426
|
|
|
@@ -402,7 +436,8 @@ function buildConfig(overrides={}) {
|
|
|
402
436
|
user: integratedAuth? "":user,
|
|
403
437
|
password: integratedAuth? "":password,
|
|
404
438
|
connectionString,
|
|
405
|
-
|
|
439
|
+
// Enforce hard read-only mode regardless of caller overrides.
|
|
440
|
+
readOnly: true,
|
|
406
441
|
maxRows: intFromEnv(firstDefined(overrides.maxRows,dbConfig?.maxRows,serverProfile?.maxRows),getMaxRowsDefault()),
|
|
407
442
|
trustServerCertificate: boolFromEnv(
|
|
408
443
|
firstDefined(
|
|
@@ -476,12 +511,9 @@ function createSqlServerConfig(config) {
|
|
|
476
511
|
};
|
|
477
512
|
|
|
478
513
|
if(config.integratedAuth) {
|
|
479
|
-
baseConfig.
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
userName: undefined,
|
|
483
|
-
password: undefined
|
|
484
|
-
}
|
|
514
|
+
baseConfig.options={
|
|
515
|
+
...baseConfig.options,
|
|
516
|
+
trustedConnection: true
|
|
485
517
|
};
|
|
486
518
|
} else {
|
|
487
519
|
baseConfig.user=config.user;
|
|
@@ -491,6 +523,36 @@ function createSqlServerConfig(config) {
|
|
|
491
523
|
return baseConfig;
|
|
492
524
|
}
|
|
493
525
|
|
|
526
|
+
function getSqlServerDriver(config) {
|
|
527
|
+
if(!config.integratedAuth) {
|
|
528
|
+
return {
|
|
529
|
+
module: sql,
|
|
530
|
+
driverName: "tedious"
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
if(process.platform!=="win32") {
|
|
535
|
+
throw new Error("SQL Server integratedAuth is only supported on Windows hosts.");
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
if(sqlMsnodesqlv8Cache===undefined) {
|
|
539
|
+
try {
|
|
540
|
+
sqlMsnodesqlv8Cache=_require("mssql/msnodesqlv8");
|
|
541
|
+
} catch {
|
|
542
|
+
sqlMsnodesqlv8Cache=null;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
if(!sqlMsnodesqlv8Cache) {
|
|
547
|
+
throw new Error("Integrated auth requires the optional dependency 'msnodesqlv8'. Install it and restart MCP.");
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
return {
|
|
551
|
+
module: sqlMsnodesqlv8Cache,
|
|
552
|
+
driverName: "msnodesqlv8"
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
|
|
494
556
|
function createPostgresConfig(config) {
|
|
495
557
|
if(config.connectionString) {
|
|
496
558
|
return {
|
|
@@ -643,9 +705,10 @@ async function connectPool(overrides={}) {
|
|
|
643
705
|
await closePoolIfAny();
|
|
644
706
|
|
|
645
707
|
if(config.engine==="sqlserver") {
|
|
708
|
+
const sqlDriver=getSqlServerDriver(config);
|
|
646
709
|
const sqlConfig=createSqlServerConfig(config);
|
|
647
|
-
const client=await new
|
|
648
|
-
connection={engine: "sqlserver",client};
|
|
710
|
+
const client=await new sqlDriver.module.ConnectionPool(sqlConfig).connect();
|
|
711
|
+
connection={engine: "sqlserver",client,sqlDriver: sqlDriver.driverName};
|
|
649
712
|
} else if(config.engine==="postgres") {
|
|
650
713
|
const pgConfig=createPostgresConfig(config);
|
|
651
714
|
const client=new PostgresPool(pgConfig);
|
|
@@ -670,8 +733,10 @@ async function connectPool(overrides={}) {
|
|
|
670
733
|
serverName: config.serverName,
|
|
671
734
|
profileSource: config.profileSource,
|
|
672
735
|
engine: config.engine,
|
|
736
|
+
sqlDriver: connection?.sqlDriver,
|
|
673
737
|
host: config.host,
|
|
674
738
|
database: config.database,
|
|
739
|
+
integratedAuth: config.integratedAuth,
|
|
675
740
|
readOnly: runtimeSettings.readOnly,
|
|
676
741
|
maxRows: runtimeSettings.maxRows
|
|
677
742
|
};
|
|
@@ -739,6 +804,9 @@ function validateQuerySafety(sqlText) {
|
|
|
739
804
|
|
|
740
805
|
const blockedPatterns=[
|
|
741
806
|
/\b(insert|update|delete|drop|alter|create|truncate|merge|exec|execute|grant|revoke)\b/i,
|
|
807
|
+
/\b(call|declare)\b/i,
|
|
808
|
+
/\b(?:sp_|xp_)[A-Za-z0-9_]*\b/i,
|
|
809
|
+
/\b[A-Za-z0-9_]+\.[A-Za-z0-9_]+\s*\(/i,
|
|
742
810
|
/--/,
|
|
743
811
|
/\/\*/
|
|
744
812
|
];
|
|
@@ -756,7 +824,9 @@ async function handleToolCall(name,args={}) {
|
|
|
756
824
|
switch(name) {
|
|
757
825
|
case TOOL_NAMES.CONNECT: {
|
|
758
826
|
const overrides={
|
|
827
|
+
...(args.target? {target: args.target}:{}),
|
|
759
828
|
...(args.alias? {alias: args.alias}:{}),
|
|
829
|
+
...(args.serverName? {serverName: args.serverName}:{}),
|
|
760
830
|
...(args.engine? {engine: args.engine}:{}),
|
|
761
831
|
...(args.connectionString? {connectionString: args.connectionString}:{}),
|
|
762
832
|
...(args.host? {host: args.host}:{}),
|
|
@@ -765,7 +835,7 @@ async function handleToolCall(name,args={}) {
|
|
|
765
835
|
...(args.database? {database: args.database}:{}),
|
|
766
836
|
...(args.user? {user: args.user}:{}),
|
|
767
837
|
...(args.password? {password: args.password}:{}),
|
|
768
|
-
...(args.
|
|
838
|
+
...(args.integratedAuth!==undefined? {integratedAuth: Boolean(args.integratedAuth)}:{}),
|
|
769
839
|
...(args.maxRows!==undefined? {maxRows: Number(args.maxRows)}:{}),
|
|
770
840
|
...(args.encrypt!==undefined? {encrypt: Boolean(args.encrypt)}:{}),
|
|
771
841
|
...(args.ssl!==undefined? {ssl: Boolean(args.ssl)}:{}),
|
|
@@ -969,6 +1039,7 @@ server.setRequestHandler(ListToolsRequestSchema,async () => {
|
|
|
969
1039
|
inputSchema: {
|
|
970
1040
|
type: "object",
|
|
971
1041
|
properties: {
|
|
1042
|
+
target: {type: "string"},
|
|
972
1043
|
alias: {type: "string"},
|
|
973
1044
|
serverName: {type: "string"},
|
|
974
1045
|
engine: {type: "string",enum: ["sqlserver","postgres","mysql"]},
|
|
@@ -979,6 +1050,7 @@ server.setRequestHandler(ListToolsRequestSchema,async () => {
|
|
|
979
1050
|
database: {type: "string"},
|
|
980
1051
|
user: {type: "string"},
|
|
981
1052
|
password: {type: "string"},
|
|
1053
|
+
integratedAuth: {type: "boolean"},
|
|
982
1054
|
encrypt: {type: "boolean"},
|
|
983
1055
|
ssl: {type: "boolean"},
|
|
984
1056
|
trustServerCertificate: {type: "boolean"},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kingsnow129/database-mcp",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"mcpName": "io.github.kingsnow129/database-mcp",
|
|
5
5
|
"description": "Database MCP server for SQL Server, PostgreSQL, and MySQL with profile-based auto resolution",
|
|
6
6
|
"author": "kingsnow129",
|
|
@@ -51,5 +51,8 @@
|
|
|
51
51
|
"mysql2": "^3.11.3",
|
|
52
52
|
"mssql": "^11.0.1",
|
|
53
53
|
"pg": "^8.13.1"
|
|
54
|
+
},
|
|
55
|
+
"optionalDependencies": {
|
|
56
|
+
"msnodesqlv8": "^4.4.0"
|
|
54
57
|
}
|
|
55
58
|
}
|