@eamonboyle/mssql-mcp 1.0.0 → 1.2.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.
Files changed (133) hide show
  1. package/README.md +153 -36
  2. package/dist/__tests__/ExplainQueryTool.test.d.ts +2 -0
  3. package/dist/__tests__/ExplainQueryTool.test.d.ts.map +1 -0
  4. package/dist/__tests__/ExplainQueryTool.test.js +96 -0
  5. package/dist/__tests__/ExplainQueryTool.test.js.map +1 -0
  6. package/dist/__tests__/config.test.d.ts +2 -0
  7. package/dist/__tests__/config.test.d.ts.map +1 -0
  8. package/dist/__tests__/config.test.js +29 -0
  9. package/dist/__tests__/config.test.js.map +1 -0
  10. package/dist/__tests__/db.test.d.ts +2 -0
  11. package/dist/__tests__/db.test.d.ts.map +1 -0
  12. package/dist/__tests__/db.test.js +204 -0
  13. package/dist/__tests__/db.test.js.map +1 -0
  14. package/dist/__tests__/promptRegistry.test.d.ts +2 -0
  15. package/dist/__tests__/promptRegistry.test.d.ts.map +1 -0
  16. package/dist/__tests__/promptRegistry.test.js +40 -0
  17. package/dist/__tests__/promptRegistry.test.js.map +1 -0
  18. package/dist/__tests__/resourceRegistry.test.d.ts +2 -0
  19. package/dist/__tests__/resourceRegistry.test.d.ts.map +1 -0
  20. package/dist/__tests__/resourceRegistry.test.js +40 -0
  21. package/dist/__tests__/resourceRegistry.test.js.map +1 -0
  22. package/dist/__tests__/schema.test.d.ts +2 -0
  23. package/dist/__tests__/schema.test.d.ts.map +1 -0
  24. package/dist/__tests__/schema.test.js +130 -0
  25. package/dist/__tests__/schema.test.js.map +1 -0
  26. package/dist/__tests__/sql.test.d.ts +2 -0
  27. package/dist/__tests__/sql.test.d.ts.map +1 -0
  28. package/dist/__tests__/sql.test.js +35 -0
  29. package/dist/__tests__/sql.test.js.map +1 -0
  30. package/dist/__tests__/toolRegistry.test.d.ts +2 -0
  31. package/dist/__tests__/toolRegistry.test.d.ts.map +1 -0
  32. package/dist/__tests__/toolRegistry.test.js +47 -0
  33. package/dist/__tests__/toolRegistry.test.js.map +1 -0
  34. package/dist/__tests__/validation.test.d.ts +2 -0
  35. package/dist/__tests__/validation.test.d.ts.map +1 -0
  36. package/dist/__tests__/validation.test.js +111 -0
  37. package/dist/__tests__/validation.test.js.map +1 -0
  38. package/dist/__tests__/writeTools.test.d.ts +2 -0
  39. package/dist/__tests__/writeTools.test.d.ts.map +1 -0
  40. package/dist/__tests__/writeTools.test.js +115 -0
  41. package/dist/__tests__/writeTools.test.js.map +1 -0
  42. package/dist/config.d.ts +5 -0
  43. package/dist/config.d.ts.map +1 -0
  44. package/dist/config.js +34 -0
  45. package/dist/config.js.map +1 -0
  46. package/dist/db.d.ts +28 -24
  47. package/dist/db.d.ts.map +1 -1
  48. package/dist/db.js +144 -89
  49. package/dist/db.js.map +1 -1
  50. package/dist/index.d.ts +2 -2
  51. package/dist/index.js +70 -105
  52. package/dist/index.js.map +1 -1
  53. package/dist/promptRegistry.d.ts +16 -0
  54. package/dist/promptRegistry.d.ts.map +1 -0
  55. package/dist/promptRegistry.js +107 -0
  56. package/dist/promptRegistry.js.map +1 -0
  57. package/dist/resourceRegistry.d.ts +12 -0
  58. package/dist/resourceRegistry.d.ts.map +1 -0
  59. package/dist/resourceRegistry.js +172 -0
  60. package/dist/resourceRegistry.js.map +1 -0
  61. package/dist/schema.d.ts +41 -0
  62. package/dist/schema.d.ts.map +1 -0
  63. package/dist/schema.js +211 -0
  64. package/dist/schema.js.map +1 -0
  65. package/dist/sql.d.ts +7 -0
  66. package/dist/sql.d.ts.map +1 -0
  67. package/dist/sql.js +73 -0
  68. package/dist/sql.js.map +1 -0
  69. package/dist/toolRegistry.d.ts +16 -0
  70. package/dist/toolRegistry.d.ts.map +1 -0
  71. package/dist/toolRegistry.js +401 -0
  72. package/dist/toolRegistry.js.map +1 -0
  73. package/dist/tools/CreateIndexTool.d.ts +30 -23
  74. package/dist/tools/CreateIndexTool.d.ts.map +1 -1
  75. package/dist/tools/CreateIndexTool.js +47 -71
  76. package/dist/tools/CreateIndexTool.js.map +1 -1
  77. package/dist/tools/CreateTableTool.d.ts +18 -11
  78. package/dist/tools/CreateTableTool.d.ts.map +1 -1
  79. package/dist/tools/CreateTableTool.js +37 -56
  80. package/dist/tools/CreateTableTool.js.map +1 -1
  81. package/dist/tools/DeleteDataTool.d.ts +22 -0
  82. package/dist/tools/DeleteDataTool.d.ts.map +1 -0
  83. package/dist/tools/DeleteDataTool.js +35 -0
  84. package/dist/tools/DeleteDataTool.js.map +1 -0
  85. package/dist/tools/DescribeObjectTool.d.ts +21 -0
  86. package/dist/tools/DescribeObjectTool.d.ts.map +1 -0
  87. package/dist/tools/DescribeObjectTool.js +31 -0
  88. package/dist/tools/DescribeObjectTool.js.map +1 -0
  89. package/dist/tools/DescribeTableTool.d.ts +19 -20
  90. package/dist/tools/DescribeTableTool.d.ts.map +1 -1
  91. package/dist/tools/DescribeTableTool.js +23 -41
  92. package/dist/tools/DescribeTableTool.js.map +1 -1
  93. package/dist/tools/DropTableTool.d.ts +13 -11
  94. package/dist/tools/DropTableTool.d.ts.map +1 -1
  95. package/dist/tools/DropTableTool.js +30 -44
  96. package/dist/tools/DropTableTool.js.map +1 -1
  97. package/dist/tools/ExplainQueryTool.d.ts +32 -0
  98. package/dist/tools/ExplainQueryTool.d.ts.map +1 -0
  99. package/dist/tools/ExplainQueryTool.js +102 -0
  100. package/dist/tools/ExplainQueryTool.js.map +1 -0
  101. package/dist/tools/InsertDataTool.d.ts +20 -16
  102. package/dist/tools/InsertDataTool.d.ts.map +1 -1
  103. package/dist/tools/InsertDataTool.js +91 -108
  104. package/dist/tools/InsertDataTool.js.map +1 -1
  105. package/dist/tools/ListObjectsTool.d.ts +20 -0
  106. package/dist/tools/ListObjectsTool.d.ts.map +1 -0
  107. package/dist/tools/ListObjectsTool.js +26 -0
  108. package/dist/tools/ListObjectsTool.js.map +1 -0
  109. package/dist/tools/ListTableTool.d.ts +20 -16
  110. package/dist/tools/ListTableTool.d.ts.map +1 -1
  111. package/dist/tools/ListTableTool.js +32 -49
  112. package/dist/tools/ListTableTool.js.map +1 -1
  113. package/dist/tools/ReadDataTool.d.ts +35 -41
  114. package/dist/tools/ReadDataTool.d.ts.map +1 -1
  115. package/dist/tools/ReadDataTool.js +92 -228
  116. package/dist/tools/ReadDataTool.js.map +1 -1
  117. package/dist/tools/SearchDataTool.d.ts +27 -0
  118. package/dist/tools/SearchDataTool.d.ts.map +1 -0
  119. package/dist/tools/SearchDataTool.js +61 -0
  120. package/dist/tools/SearchDataTool.js.map +1 -0
  121. package/dist/tools/UpdateDataTool.d.ts +21 -16
  122. package/dist/tools/UpdateDataTool.d.ts.map +1 -1
  123. package/dist/tools/UpdateDataTool.js +45 -65
  124. package/dist/tools/UpdateDataTool.js.map +1 -1
  125. package/dist/validation.d.ts +14 -0
  126. package/dist/validation.d.ts.map +1 -0
  127. package/dist/validation.js +134 -0
  128. package/dist/validation.js.map +1 -0
  129. package/dist/writeSafety.d.ts +21 -0
  130. package/dist/writeSafety.d.ts.map +1 -0
  131. package/dist/writeSafety.js +169 -0
  132. package/dist/writeSafety.js.map +1 -0
  133. package/package.json +17 -6
@@ -1,17 +1,22 @@
1
- import { Tool } from "@modelcontextprotocol/sdk/types.js";
2
- export declare class UpdateDataTool implements Tool {
3
- [key: string]: any;
4
- name: string;
5
- description: string;
6
- inputSchema: any;
7
- run(params: any): Promise<{
8
- success: boolean;
9
- message: string;
10
- rowsAffected?: undefined;
11
- } | {
12
- success: boolean;
13
- message: string;
14
- rowsAffected: number;
15
- }>;
16
- }
1
+ import { type SqlFilter } from "../writeSafety.js";
2
+ interface UpdateDataParams {
3
+ tableName: string;
4
+ updates: Record<string, unknown>;
5
+ filters: SqlFilter[];
6
+ databaseName?: string;
7
+ }
8
+ export declare class UpdateDataTool {
9
+ name: string;
10
+ description: string;
11
+ run(params: UpdateDataParams): Promise<{
12
+ success: boolean;
13
+ message: string;
14
+ rowsAffected?: undefined;
15
+ } | {
16
+ success: boolean;
17
+ message: string;
18
+ rowsAffected: number;
19
+ }>;
20
+ }
21
+ export {};
17
22
  //# sourceMappingURL=UpdateDataTool.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"UpdateDataTool.d.ts","sourceRoot":"","sources":["../../src/tools/UpdateDataTool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAG1D,qBAAa,cAAe,YAAW,IAAI;IACzC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;IACnB,IAAI,SAAiB;IACrB,WAAW,SAAmH;IAC9H,WAAW,MAqBF;IAEH,GAAG,CAAC,MAAM,EAAE,GAAG;;;;;;;;;CAwCtB"}
1
+ {"version":3,"file":"UpdateDataTool.d.ts","sourceRoot":"","sources":["../../src/tools/UpdateDataTool.ts"],"names":[],"mappings":"AAEA,OAAO,EAAiC,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAElF,UAAU,gBAAgB;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,OAAO,EAAE,SAAS,EAAE,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,qBAAa,cAAc;IACzB,IAAI,SAAiB;IACrB,WAAW,SACoE;IAEzE,GAAG,CAAC,MAAM,EAAE,gBAAgB;;;;;;;;;CA6CnC"}
@@ -1,66 +1,46 @@
1
- import { getSqlRequest } from "../db.js";
2
- export class UpdateDataTool {
3
- constructor() {
4
- this.name = "update_data";
5
- this.description = "Updates data in an MSSQL Database table using a WHERE clause. The WHERE clause must be provided for security.";
6
- this.inputSchema = {
7
- type: "object",
8
- properties: {
9
- tableName: {
10
- type: "string",
11
- description: "Name of the table to update"
12
- },
13
- updates: {
14
- type: "object",
15
- description: "Key-value pairs of columns to update. Example: { 'status': 'active', 'last_updated': '2025-01-01' }",
16
- },
17
- whereClause: {
18
- type: "string",
19
- description: "WHERE clause to identify which records to update. Example: \"genre = 'comedy' AND created_date <= '2025-07-05'\""
20
- },
21
- databaseName: {
22
- type: "string",
23
- description: "Name of the database to use (optional). Omit to use the default database."
24
- },
25
- },
26
- required: ["tableName", "updates", "whereClause"],
27
- };
28
- }
29
- async run(params) {
30
- let query;
31
- try {
32
- const { tableName, updates, whereClause, databaseName } = params;
33
- const { request, error } = await getSqlRequest(databaseName);
34
- if (error) {
35
- return { success: false, message: error };
36
- }
37
- // Basic validation: ensure whereClause is not empty
38
- if (!whereClause || whereClause.trim() === '') {
39
- throw new Error("WHERE clause is required for security reasons");
40
- }
41
- // Build SET clause with parameterized queries for security
42
- const setClause = Object.keys(updates)
43
- .map((key, index) => {
44
- const paramName = `update_${index}`;
45
- request.input(paramName, updates[key]);
46
- return `[${key}] = @${paramName}`;
47
- })
48
- .join(", ");
49
- query = `UPDATE ${tableName} SET ${setClause} WHERE ${whereClause}`;
50
- const result = await request.query(query);
51
- return {
52
- success: true,
53
- message: `Update completed successfully. ${result.rowsAffected[0]} row(s) affected`,
54
- rowsAffected: result.rowsAffected[0],
55
- };
56
- }
57
- catch (error) {
58
- console.error("Error updating data:", error);
59
- return {
60
- success: false,
61
- message: `Failed to update data ${query ? ` with '${query}'` : ''}: ${error}`,
62
- };
63
- }
64
- }
65
- }
1
+ import { getSqlRequest } from "../db.js";
2
+ import { buildQualifiedName, quoteIdentifier } from "../sql.js";
3
+ import { buildParameterizedWhereClause } from "../writeSafety.js";
4
+ export class UpdateDataTool {
5
+ constructor() {
6
+ this.name = "update_data";
7
+ this.description = "Updates data in an MSSQL Database table using required structured filters.";
8
+ }
9
+ async run(params) {
10
+ let query;
11
+ try {
12
+ const { tableName, updates, filters, databaseName } = params;
13
+ const { request, error } = await getSqlRequest(databaseName);
14
+ if (error) {
15
+ return { success: false, message: error };
16
+ }
17
+ const updateColumns = Object.keys(updates);
18
+ if (updateColumns.length === 0) {
19
+ throw new Error("At least one column update is required.");
20
+ }
21
+ const setClause = updateColumns
22
+ .map((key, index) => {
23
+ const paramName = `update_${index}`;
24
+ request.input(paramName, updates[key]);
25
+ return `${quoteIdentifier(key)} = @${paramName}`;
26
+ })
27
+ .join(", ");
28
+ const whereClause = buildParameterizedWhereClause(filters, (name, value) => request.input(name, value), "update_filter");
29
+ query = `UPDATE ${buildQualifiedName(tableName)} SET ${setClause} WHERE ${whereClause}`;
30
+ const result = await request.query(query);
31
+ return {
32
+ success: true,
33
+ message: `Update completed successfully. ${result.rowsAffected[0]} row(s) affected`,
34
+ rowsAffected: result.rowsAffected[0],
35
+ };
36
+ }
37
+ catch (error) {
38
+ console.error("Error updating data:", error);
39
+ return {
40
+ success: false,
41
+ message: `Failed to update data ${query ? ` with '${query}'` : ""}: ${error}`,
42
+ };
43
+ }
44
+ }
45
+ }
66
46
  //# sourceMappingURL=UpdateDataTool.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"UpdateDataTool.js","sourceRoot":"","sources":["../../src/tools/UpdateDataTool.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,OAAO,cAAc;IAA3B;QAEE,SAAI,GAAG,aAAa,CAAC;QACrB,gBAAW,GAAG,+GAA+G,CAAC;QAC9H,gBAAW,GAAG;YACZ,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,SAAS,EAAE;oBACT,IAAI,EAAE,QAAQ;oBACd,WAAW,EAAE,6BAA6B;iBAC3C;gBACD,OAAO,EAAE;oBACP,IAAI,EAAE,QAAQ;oBACd,WAAW,EAAE,qGAAqG;iBACnH;gBACD,WAAW,EAAE;oBACX,IAAI,EAAE,QAAQ;oBACd,WAAW,EAAE,kHAAkH;iBAChI;gBACD,YAAY,EAAE;oBACZ,IAAI,EAAE,QAAQ;oBACd,WAAW,EAAE,2EAA2E;iBACzF;aACF;YACD,QAAQ,EAAE,CAAC,WAAW,EAAE,SAAS,EAAE,aAAa,CAAC;SAC3C,CAAC;IA0CX,CAAC;IAxCC,KAAK,CAAC,GAAG,CAAC,MAAW;QACnB,IAAI,KAAyB,CAAC;QAC9B,IAAI;YACF,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,GAAG,MAAM,CAAC;YAEjE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,aAAa,CAAC,YAAY,CAAC,CAAC;YAC7D,IAAI,KAAK,EAAE;gBACT,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;aAC3C;YAED,oDAAoD;YACpD,IAAI,CAAC,WAAW,IAAI,WAAW,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;gBAC7C,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;aAClE;YAED,2DAA2D;YAC3D,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC;iBACnC,GAAG,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE;gBAClB,MAAM,SAAS,GAAG,UAAU,KAAK,EAAE,CAAC;gBACpC,OAAO,CAAC,KAAK,CAAC,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;gBACvC,OAAO,IAAI,GAAG,QAAQ,SAAS,EAAE,CAAC;YACpC,CAAC,CAAC;iBACD,IAAI,CAAC,IAAI,CAAC,CAAC;YAEd,KAAK,GAAG,UAAU,SAAS,QAAQ,SAAS,UAAU,WAAW,EAAE,CAAC;YACpE,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAE1C,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,kCAAkC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,kBAAkB;gBACnF,YAAY,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;aACrC,CAAC;SACH;QAAC,OAAO,KAAK,EAAE;YACd,OAAO,CAAC,KAAK,CAAC,sBAAsB,EAAE,KAAK,CAAC,CAAC;YAC7C,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE,yBAAyB,KAAK,CAAC,CAAC,CAAC,UAAU,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,EAAE;aAC9E,CAAC;SACH;IACH,CAAC;CACF"}
1
+ {"version":3,"file":"UpdateDataTool.js","sourceRoot":"","sources":["../../src/tools/UpdateDataTool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAChE,OAAO,EAAE,6BAA6B,EAAkB,MAAM,mBAAmB,CAAC;AASlF,MAAM,OAAO,cAAc;IAA3B;QACE,SAAI,GAAG,aAAa,CAAC;QACrB,gBAAW,GACT,4EAA4E,CAAC;IA+CjF,CAAC;IA7CC,KAAK,CAAC,GAAG,CAAC,MAAwB;QAChC,IAAI,KAAyB,CAAC;QAC9B,IAAI,CAAC;YACH,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,MAAM,CAAC;YAE7D,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,aAAa,CAAC,YAAY,CAAC,CAAC;YAC7D,IAAI,KAAK,EAAE,CAAC;gBACV,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;YAC5C,CAAC;YAED,MAAM,aAAa,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC3C,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC/B,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;YAC7D,CAAC;YAED,MAAM,SAAS,GAAG,aAAa;iBAC5B,GAAG,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE;gBAClB,MAAM,SAAS,GAAG,UAAU,KAAK,EAAE,CAAC;gBACpC,OAAO,CAAC,KAAK,CAAC,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;gBACvC,OAAO,GAAG,eAAe,CAAC,GAAG,CAAC,OAAO,SAAS,EAAE,CAAC;YACnD,CAAC,CAAC;iBACD,IAAI,CAAC,IAAI,CAAC,CAAC;YAEd,MAAM,WAAW,GAAG,6BAA6B,CAC/C,OAAO,EACP,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,EAC3C,eAAe,CAChB,CAAC;YAEF,KAAK,GAAG,UAAU,kBAAkB,CAAC,SAAS,CAAC,QAAQ,SAAS,UAAU,WAAW,EAAE,CAAC;YACxF,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAE1C,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,kCAAkC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,kBAAkB;gBACnF,YAAY,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;aACrC,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,sBAAsB,EAAE,KAAK,CAAC,CAAC;YAC7C,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE,yBAAyB,KAAK,CAAC,CAAC,CAAC,UAAU,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,EAAE;aAC9E,CAAC;QACJ,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * SQL query validation for read-only operations.
3
+ * Extracted for testability and reuse.
4
+ */
5
+ export interface ValidationResult {
6
+ isValid: boolean;
7
+ error?: string;
8
+ }
9
+ /**
10
+ * Validates a SQL query for read-only (SELECT) operations.
11
+ * Rejects dangerous keywords, injection patterns, and multi-statement queries.
12
+ */
13
+ export declare function validateReadQuery(query: string): ValidationResult;
14
+ //# sourceMappingURL=validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA8DH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,CAoFjE"}
@@ -0,0 +1,134 @@
1
+ /**
2
+ * SQL query validation for read-only operations.
3
+ * Extracted for testability and reuse.
4
+ */
5
+ const DANGEROUS_KEYWORDS = [
6
+ "DELETE",
7
+ "DROP",
8
+ "UPDATE",
9
+ "INSERT",
10
+ "ALTER",
11
+ "CREATE",
12
+ "TRUNCATE",
13
+ "EXEC",
14
+ "EXECUTE",
15
+ "MERGE",
16
+ "REPLACE",
17
+ "GRANT",
18
+ "REVOKE",
19
+ "COMMIT",
20
+ "ROLLBACK",
21
+ "TRANSACTION",
22
+ "BEGIN",
23
+ "DECLARE",
24
+ "SET",
25
+ "USE",
26
+ "BACKUP",
27
+ "RESTORE",
28
+ "KILL",
29
+ "SHUTDOWN",
30
+ "WAITFOR",
31
+ "OPENROWSET",
32
+ "OPENDATASOURCE",
33
+ "OPENQUERY",
34
+ "OPENXML",
35
+ "BULK",
36
+ "INTO",
37
+ ];
38
+ const DANGEROUS_PATTERNS = [
39
+ /SELECT\s+.*?\s+INTO\s+/i,
40
+ /;\s*(DELETE|DROP|UPDATE|INSERT|ALTER|CREATE|TRUNCATE|EXEC|EXECUTE|MERGE|REPLACE|GRANT|REVOKE)/i,
41
+ /UNION\s+(?:ALL\s+)?SELECT.*?(DELETE|DROP|UPDATE|INSERT|ALTER|CREATE|TRUNCATE|EXEC|EXECUTE)/i,
42
+ /--.*?(DELETE|DROP|UPDATE|INSERT|ALTER|CREATE|TRUNCATE|EXEC|EXECUTE)/i,
43
+ /\/\*.*?(DELETE|DROP|UPDATE|INSERT|ALTER|CREATE|TRUNCATE|EXEC|EXECUTE).*?\*\//i,
44
+ /EXEC\s*\(/i,
45
+ /EXECUTE\s*\(/i,
46
+ /sp_/i,
47
+ /xp_/i,
48
+ /BULK\s+INSERT/i,
49
+ /OPENROWSET/i,
50
+ /OPENDATASOURCE/i,
51
+ /@@/,
52
+ /SYSTEM_USER/i,
53
+ /USER_NAME/i,
54
+ /DB_NAME/i,
55
+ /HOST_NAME/i,
56
+ /WAITFOR\s+DELAY/i,
57
+ /WAITFOR\s+TIME/i,
58
+ /;\s*\w/,
59
+ /\+\s*CHAR\s*\(/i,
60
+ /\+\s*NCHAR\s*\(/i,
61
+ /\+\s*ASCII\s*\(/i,
62
+ ];
63
+ /**
64
+ * Validates a SQL query for read-only (SELECT) operations.
65
+ * Rejects dangerous keywords, injection patterns, and multi-statement queries.
66
+ */
67
+ export function validateReadQuery(query) {
68
+ if (!query || typeof query !== "string") {
69
+ return {
70
+ isValid: false,
71
+ error: "Query must be a non-empty string",
72
+ };
73
+ }
74
+ const cleanQuery = query
75
+ .replace(/--.*$/gm, "")
76
+ .replace(/\/\*[\s\S]*?\*\//g, "")
77
+ .replace(/\s+/g, " ")
78
+ .trim();
79
+ if (!cleanQuery) {
80
+ return {
81
+ isValid: false,
82
+ error: "Query cannot be empty after removing comments",
83
+ };
84
+ }
85
+ const upperQuery = cleanQuery.toUpperCase();
86
+ if (!upperQuery.startsWith("SELECT")) {
87
+ return {
88
+ isValid: false,
89
+ error: "Query must start with SELECT for security reasons",
90
+ };
91
+ }
92
+ for (const keyword of DANGEROUS_KEYWORDS) {
93
+ const keywordRegex = new RegExp(`(^|\\s|[^A-Za-z0-9_])${keyword}($|\\s|[^A-Za-z0-9_])`, "i");
94
+ if (keywordRegex.test(upperQuery)) {
95
+ return {
96
+ isValid: false,
97
+ error: `Dangerous keyword '${keyword}' detected in query. Only SELECT operations are allowed.`,
98
+ };
99
+ }
100
+ }
101
+ for (const pattern of DANGEROUS_PATTERNS) {
102
+ if (pattern.test(query)) {
103
+ return {
104
+ isValid: false,
105
+ error: "Potentially malicious SQL pattern detected. Only simple SELECT queries are allowed.",
106
+ };
107
+ }
108
+ }
109
+ const statements = cleanQuery
110
+ .split(";")
111
+ .filter((stmt) => stmt.trim().length > 0);
112
+ if (statements.length > 1) {
113
+ return {
114
+ isValid: false,
115
+ error: "Multiple SQL statements are not allowed. Use only a single SELECT statement.",
116
+ };
117
+ }
118
+ if (query.includes("CHAR(") ||
119
+ query.includes("NCHAR(") ||
120
+ query.includes("ASCII(")) {
121
+ return {
122
+ isValid: false,
123
+ error: "Character conversion functions are not allowed as they may be used for obfuscation.",
124
+ };
125
+ }
126
+ if (query.length > 10000) {
127
+ return {
128
+ isValid: false,
129
+ error: "Query is too long. Maximum allowed length is 10,000 characters.",
130
+ };
131
+ }
132
+ return { isValid: true };
133
+ }
134
+ //# sourceMappingURL=validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.js","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,kBAAkB,GAAG;IACzB,QAAQ;IACR,MAAM;IACN,QAAQ;IACR,QAAQ;IACR,OAAO;IACP,QAAQ;IACR,UAAU;IACV,MAAM;IACN,SAAS;IACT,OAAO;IACP,SAAS;IACT,OAAO;IACP,QAAQ;IACR,QAAQ;IACR,UAAU;IACV,aAAa;IACb,OAAO;IACP,SAAS;IACT,KAAK;IACL,KAAK;IACL,QAAQ;IACR,SAAS;IACT,MAAM;IACN,UAAU;IACV,SAAS;IACT,YAAY;IACZ,gBAAgB;IAChB,WAAW;IACX,SAAS;IACT,MAAM;IACN,MAAM;CACP,CAAC;AAEF,MAAM,kBAAkB,GAAG;IACzB,yBAAyB;IACzB,gGAAgG;IAChG,6FAA6F;IAC7F,sEAAsE;IACtE,+EAA+E;IAC/E,YAAY;IACZ,eAAe;IACf,MAAM;IACN,MAAM;IACN,gBAAgB;IAChB,aAAa;IACb,iBAAiB;IACjB,IAAI;IACJ,cAAc;IACd,YAAY;IACZ,UAAU;IACV,YAAY;IACZ,kBAAkB;IAClB,iBAAiB;IACjB,QAAQ;IACR,iBAAiB;IACjB,kBAAkB;IAClB,kBAAkB;CACnB,CAAC;AAOF;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAa;IAC7C,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACxC,OAAO;YACL,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,kCAAkC;SAC1C,CAAC;IACJ,CAAC;IAED,MAAM,UAAU,GAAG,KAAK;SACrB,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC;SACtB,OAAO,CAAC,mBAAmB,EAAE,EAAE,CAAC;SAChC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE,CAAC;IAEV,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO;YACL,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,+CAA+C;SACvD,CAAC;IACJ,CAAC;IAED,MAAM,UAAU,GAAG,UAAU,CAAC,WAAW,EAAE,CAAC;IAE5C,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QACrC,OAAO;YACL,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,mDAAmD;SAC3D,CAAC;IACJ,CAAC;IAED,KAAK,MAAM,OAAO,IAAI,kBAAkB,EAAE,CAAC;QACzC,MAAM,YAAY,GAAG,IAAI,MAAM,CAC7B,wBAAwB,OAAO,uBAAuB,EACtD,GAAG,CACJ,CAAC;QACF,IAAI,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YAClC,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,sBAAsB,OAAO,0DAA0D;aAC/F,CAAC;QACJ,CAAC;IACH,CAAC;IAED,KAAK,MAAM,OAAO,IAAI,kBAAkB,EAAE,CAAC;QACzC,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACxB,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,KAAK,EACH,qFAAqF;aACxF,CAAC;QACJ,CAAC;IACH,CAAC;IAED,MAAM,UAAU,GAAG,UAAU;SAC1B,KAAK,CAAC,GAAG,CAAC;SACV,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC5C,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,OAAO;YACL,OAAO,EAAE,KAAK;YACd,KAAK,EACH,8EAA8E;SACjF,CAAC;IACJ,CAAC;IAED,IACE,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC;QACvB,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC;QACxB,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,EACxB,CAAC;QACD,OAAO;YACL,OAAO,EAAE,KAAK;YACd,KAAK,EACH,qFAAqF;SACxF,CAAC;IACJ,CAAC;IAED,IAAI,KAAK,CAAC,MAAM,GAAG,KAAK,EAAE,CAAC;QACzB,OAAO;YACL,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,iEAAiE;SACzE,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC3B,CAAC"}
@@ -0,0 +1,21 @@
1
+ export declare const SQL_FILTER_OPERATORS: readonly ["=", "!=", ">", ">=", "<", "<=", "LIKE", "IN", "IS NULL", "IS NOT NULL"];
2
+ export type SqlFilterOperator = (typeof SQL_FILTER_OPERATORS)[number];
3
+ export interface SqlFilter {
4
+ column: string;
5
+ operator: SqlFilterOperator;
6
+ value?: unknown;
7
+ values?: unknown[];
8
+ }
9
+ export interface SqlTypeDeclaration {
10
+ type: string;
11
+ nullable?: boolean;
12
+ isPrimaryKey?: boolean;
13
+ isUnique?: boolean;
14
+ isIdentity?: boolean;
15
+ identitySeed?: number;
16
+ identityIncrement?: number;
17
+ }
18
+ export declare function normalizeSqlType(rawType: string): string;
19
+ export declare function buildCreateColumnDefinition(columnName: string, declaration: SqlTypeDeclaration): string;
20
+ export declare function buildParameterizedWhereClause(filters: SqlFilter[], input: (name: string, value: unknown) => unknown, parameterPrefix?: string): string;
21
+ //# sourceMappingURL=writeSafety.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"writeSafety.d.ts","sourceRoot":"","sources":["../src/writeSafety.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,oBAAoB,oFAWvB,CAAC;AAGX,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEtE,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAqFD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CA6BxD;AAED,wBAAgB,2BAA2B,CACzC,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,kBAAkB,GAC9B,MAAM,CAwBR;AAED,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,SAAS,EAAE,EACpB,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,OAAO,EAChD,eAAe,SAAW,GACzB,MAAM,CA0CR"}
@@ -0,0 +1,169 @@
1
+ import { quoteIdentifier } from "./sql.js";
2
+ export const SQL_FILTER_OPERATORS = [
3
+ "=",
4
+ "!=",
5
+ ">",
6
+ ">=",
7
+ "<",
8
+ "<=",
9
+ "LIKE",
10
+ "IN",
11
+ "IS NULL",
12
+ "IS NOT NULL",
13
+ ];
14
+ const FILTER_OPERATOR_SET = new Set(SQL_FILTER_OPERATORS);
15
+ const LENGTH_TYPES = new Set([
16
+ "binary",
17
+ "char",
18
+ "nchar",
19
+ "nvarchar",
20
+ "varbinary",
21
+ "varchar",
22
+ ]);
23
+ const PRECISION_SCALE_TYPES = new Set(["decimal", "numeric"]);
24
+ const OPTIONAL_PRECISION_TYPES = new Set(["float"]);
25
+ const OPTIONAL_SCALE_TYPES = new Set(["datetime2", "datetimeoffset", "time"]);
26
+ const SIMPLE_TYPES = new Set([
27
+ "bigint",
28
+ "bit",
29
+ "date",
30
+ "datetime",
31
+ "image",
32
+ "int",
33
+ "money",
34
+ "ntext",
35
+ "real",
36
+ "smalldatetime",
37
+ "smallint",
38
+ "smallmoney",
39
+ "sql_variant",
40
+ "text",
41
+ "tinyint",
42
+ "uniqueidentifier",
43
+ "xml",
44
+ ]);
45
+ function assertPositiveInteger(value, label) {
46
+ if (!Number.isInteger(value) || value < 0) {
47
+ throw new Error(`Invalid ${label}.`);
48
+ }
49
+ }
50
+ function normalizeTypeParameterList(typeName, params) {
51
+ if (LENGTH_TYPES.has(typeName)) {
52
+ const normalized = params.trim().toUpperCase();
53
+ if (normalized === "MAX") {
54
+ return "(MAX)";
55
+ }
56
+ const parsed = Number.parseInt(normalized, 10);
57
+ if (!Number.isInteger(parsed) || parsed <= 0) {
58
+ throw new Error(`Invalid length for SQL type '${typeName}'.`);
59
+ }
60
+ return `(${parsed})`;
61
+ }
62
+ if (PRECISION_SCALE_TYPES.has(typeName)) {
63
+ const match = params.match(/^\s*(\d+)\s*,\s*(\d+)\s*$/);
64
+ if (!match) {
65
+ throw new Error(`Invalid precision/scale for SQL type '${typeName}'.`);
66
+ }
67
+ return `(${Number.parseInt(match[1], 10)}, ${Number.parseInt(match[2], 10)})`;
68
+ }
69
+ if (OPTIONAL_PRECISION_TYPES.has(typeName)) {
70
+ const parsed = Number.parseInt(params.trim(), 10);
71
+ if (!Number.isInteger(parsed) || parsed <= 0) {
72
+ throw new Error(`Invalid precision for SQL type '${typeName}'.`);
73
+ }
74
+ return `(${parsed})`;
75
+ }
76
+ if (OPTIONAL_SCALE_TYPES.has(typeName)) {
77
+ const parsed = Number.parseInt(params.trim(), 10);
78
+ if (!Number.isInteger(parsed) || parsed < 0) {
79
+ throw new Error(`Invalid scale for SQL type '${typeName}'.`);
80
+ }
81
+ return `(${parsed})`;
82
+ }
83
+ throw new Error(`SQL type '${typeName}' does not accept parameters.`);
84
+ }
85
+ export function normalizeSqlType(rawType) {
86
+ if (typeof rawType !== "string" || rawType.trim() === "") {
87
+ throw new Error("Column type is required.");
88
+ }
89
+ const trimmed = rawType.trim();
90
+ const match = trimmed.match(/^([A-Za-z][A-Za-z0-9_]*)(?:\(([^)]+)\))?$/);
91
+ if (!match) {
92
+ throw new Error(`Unsupported SQL type declaration '${rawType}'.`);
93
+ }
94
+ const typeName = match[1].toLowerCase();
95
+ const rawParams = match[2];
96
+ if (!LENGTH_TYPES.has(typeName) &&
97
+ !PRECISION_SCALE_TYPES.has(typeName) &&
98
+ !OPTIONAL_PRECISION_TYPES.has(typeName) &&
99
+ !OPTIONAL_SCALE_TYPES.has(typeName) &&
100
+ !SIMPLE_TYPES.has(typeName)) {
101
+ throw new Error(`Unsupported SQL type '${rawType}'.`);
102
+ }
103
+ if (!rawParams) {
104
+ return typeName.toUpperCase();
105
+ }
106
+ return `${typeName.toUpperCase()}${normalizeTypeParameterList(typeName, rawParams)}`;
107
+ }
108
+ export function buildCreateColumnDefinition(columnName, declaration) {
109
+ const fragments = [quoteIdentifier(columnName), normalizeSqlType(declaration.type)];
110
+ if (declaration.isIdentity) {
111
+ const seed = declaration.identitySeed ?? 1;
112
+ const increment = declaration.identityIncrement ?? 1;
113
+ assertPositiveInteger(seed, "identity seed");
114
+ assertPositiveInteger(increment, "identity increment");
115
+ fragments.push(`IDENTITY(${seed}, ${increment})`);
116
+ }
117
+ if (declaration.isPrimaryKey) {
118
+ fragments.push("PRIMARY KEY");
119
+ }
120
+ else if (declaration.isUnique) {
121
+ fragments.push("UNIQUE");
122
+ }
123
+ if (declaration.nullable === false || declaration.isPrimaryKey) {
124
+ fragments.push("NOT NULL");
125
+ }
126
+ else if (declaration.nullable === true) {
127
+ fragments.push("NULL");
128
+ }
129
+ return fragments.join(" ");
130
+ }
131
+ export function buildParameterizedWhereClause(filters, input, parameterPrefix = "filter") {
132
+ if (!Array.isArray(filters) || filters.length === 0) {
133
+ throw new Error("At least one filter is required.");
134
+ }
135
+ return filters
136
+ .map((filter, index) => {
137
+ const columnName = quoteIdentifier(filter.column);
138
+ const operator = filter.operator;
139
+ if (!FILTER_OPERATOR_SET.has(operator)) {
140
+ throw new Error(`Unsupported filter operator '${operator}'.`);
141
+ }
142
+ switch (operator) {
143
+ case "IS NULL":
144
+ case "IS NOT NULL":
145
+ return `${columnName} ${operator}`;
146
+ case "IN": {
147
+ if (!Array.isArray(filter.values) || filter.values.length === 0) {
148
+ throw new Error(`Filter '${filter.column}' requires a non-empty 'values' array.`);
149
+ }
150
+ const placeholders = filter.values.map((value, valueIndex) => {
151
+ const parameterName = `${parameterPrefix}_${index}_${valueIndex}`;
152
+ input(parameterName, value);
153
+ return `@${parameterName}`;
154
+ });
155
+ return `${columnName} IN (${placeholders.join(", ")})`;
156
+ }
157
+ default: {
158
+ if (!("value" in filter)) {
159
+ throw new Error(`Filter '${filter.column}' requires a 'value'.`);
160
+ }
161
+ const parameterName = `${parameterPrefix}_${index}`;
162
+ input(parameterName, filter.value);
163
+ return `${columnName} ${operator} @${parameterName}`;
164
+ }
165
+ }
166
+ })
167
+ .join(" AND ");
168
+ }
169
+ //# sourceMappingURL=writeSafety.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"writeSafety.js","sourceRoot":"","sources":["../src/writeSafety.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eamonboyle/mssql-mcp",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "MCP server for Microsoft SQL Server - query and manage your database with natural language in Cursor, Claude, and VS Code",
5
5
  "keywords": [
6
6
  "mcp",
@@ -29,7 +29,11 @@
29
29
  "build": "tsc && shx chmod +x dist/*.js",
30
30
  "prepare": "npm run build",
31
31
  "watch": "tsc --watch",
32
- "start": "node dist/index.js"
32
+ "start": "node dist/index.js",
33
+ "test": "vitest run",
34
+ "test:watch": "vitest",
35
+ "lint": "eslint src",
36
+ "format": "prettier --write src"
33
37
  },
34
38
  "files": [
35
39
  "dist"
@@ -38,15 +42,22 @@
38
42
  "node": ">=18.0.0"
39
43
  },
40
44
  "devDependencies": {
45
+ "@eslint/js": "^9.39.2",
41
46
  "@types/mssql": "^9.1.7",
42
- "@types/node": "^16.7.13",
47
+ "@types/node": "^20.0.0",
48
+ "eslint": "^9.39.2",
49
+ "eslint-config-prettier": "^10.1.8",
50
+ "prettier": "^3.8.1",
43
51
  "shx": "^0.3.4",
44
- "typescript": "^4.9.5"
52
+ "typescript": "^5.9.3",
53
+ "typescript-eslint": "^8.55.0",
54
+ "vitest": "^2.1.0"
45
55
  },
46
56
  "dependencies": {
47
- "@modelcontextprotocol/sdk": "^1.0.0",
57
+ "@modelcontextprotocol/sdk": "^1.28.0",
48
58
  "dotenv": "^10.0.0",
49
- "mssql": "^11.0.1"
59
+ "mssql": "^11.0.1",
60
+ "zod": "^4.3.6"
50
61
  },
51
62
  "type": "module"
52
63
  }