@eamonboyle/mssql-mcp 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +195 -0
- package/dist/db.d.ts +25 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +90 -0
- package/dist/db.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +106 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/CreateIndexTool.d.ts +24 -0
- package/dist/tools/CreateIndexTool.d.ts.map +1 -0
- package/dist/tools/CreateIndexTool.js +72 -0
- package/dist/tools/CreateIndexTool.js.map +1 -0
- package/dist/tools/CreateTableTool.d.ts +12 -0
- package/dist/tools/CreateTableTool.d.ts.map +1 -0
- package/dist/tools/CreateTableTool.js +57 -0
- package/dist/tools/CreateTableTool.js.map +1 -0
- package/dist/tools/DescribeTableTool.d.ts +21 -0
- package/dist/tools/DescribeTableTool.d.ts.map +1 -0
- package/dist/tools/DescribeTableTool.js +42 -0
- package/dist/tools/DescribeTableTool.js.map +1 -0
- package/dist/tools/DropTableTool.d.ts +12 -0
- package/dist/tools/DropTableTool.d.ts.map +1 -0
- package/dist/tools/DropTableTool.js +45 -0
- package/dist/tools/DropTableTool.js.map +1 -0
- package/dist/tools/InsertDataTool.d.ts +17 -0
- package/dist/tools/InsertDataTool.d.ts.map +1 -0
- package/dist/tools/InsertDataTool.js +151 -0
- package/dist/tools/InsertDataTool.js.map +1 -0
- package/dist/tools/ListTableTool.d.ts +17 -0
- package/dist/tools/ListTableTool.d.ts.map +1 -0
- package/dist/tools/ListTableTool.js +50 -0
- package/dist/tools/ListTableTool.js.map +1 -0
- package/dist/tools/ReadDataTool.d.ts +42 -0
- package/dist/tools/ReadDataTool.d.ts.map +1 -0
- package/dist/tools/ReadDataTool.js +229 -0
- package/dist/tools/ReadDataTool.js.map +1 -0
- package/dist/tools/UpdateDataTool.d.ts +17 -0
- package/dist/tools/UpdateDataTool.d.ts.map +1 -0
- package/dist/tools/UpdateDataTool.js +66 -0
- package/dist/tools/UpdateDataTool.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { getSqlRequest } from "../db.js";
|
|
2
|
+
export class ReadDataTool {
|
|
3
|
+
constructor() {
|
|
4
|
+
this.name = "read_data";
|
|
5
|
+
this.description = "Executes a SELECT query on an MSSQL Database table. The query must start with SELECT and cannot contain any destructive SQL operations for security reasons.";
|
|
6
|
+
this.inputSchema = {
|
|
7
|
+
type: "object",
|
|
8
|
+
properties: {
|
|
9
|
+
query: {
|
|
10
|
+
type: "string",
|
|
11
|
+
description: "SQL SELECT query to execute (must start with SELECT and cannot contain destructive operations). Example: SELECT * FROM movies WHERE genre = 'comedy'"
|
|
12
|
+
},
|
|
13
|
+
databaseName: {
|
|
14
|
+
type: "string",
|
|
15
|
+
description: "Name of the database to query (optional). Omit to use the default database."
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
required: ["query"],
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Validates the SQL query for security issues
|
|
23
|
+
* @param query The SQL query to validate
|
|
24
|
+
* @returns Validation result with success flag and error message if invalid
|
|
25
|
+
*/
|
|
26
|
+
validateQuery(query) {
|
|
27
|
+
if (!query || typeof query !== 'string') {
|
|
28
|
+
return {
|
|
29
|
+
isValid: false,
|
|
30
|
+
error: 'Query must be a non-empty string'
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
// Remove comments and normalize whitespace for analysis
|
|
34
|
+
const cleanQuery = query
|
|
35
|
+
.replace(/--.*$/gm, '') // Remove line comments
|
|
36
|
+
.replace(/\/\*[\s\S]*?\*\//g, '') // Remove block comments
|
|
37
|
+
.replace(/\s+/g, ' ') // Normalize whitespace
|
|
38
|
+
.trim();
|
|
39
|
+
if (!cleanQuery) {
|
|
40
|
+
return {
|
|
41
|
+
isValid: false,
|
|
42
|
+
error: 'Query cannot be empty after removing comments'
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
const upperQuery = cleanQuery.toUpperCase();
|
|
46
|
+
// Must start with SELECT
|
|
47
|
+
if (!upperQuery.startsWith('SELECT')) {
|
|
48
|
+
return {
|
|
49
|
+
isValid: false,
|
|
50
|
+
error: 'Query must start with SELECT for security reasons'
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
// Check for dangerous keywords in the cleaned query using word boundaries
|
|
54
|
+
for (const keyword of ReadDataTool.DANGEROUS_KEYWORDS) {
|
|
55
|
+
// Use word boundary regex to match only complete keywords, not parts of words
|
|
56
|
+
const keywordRegex = new RegExp(`(^|\\s|[^A-Za-z0-9_])${keyword}($|\\s|[^A-Za-z0-9_])`, 'i');
|
|
57
|
+
if (keywordRegex.test(upperQuery)) {
|
|
58
|
+
return {
|
|
59
|
+
isValid: false,
|
|
60
|
+
error: `Dangerous keyword '${keyword}' detected in query. Only SELECT operations are allowed.`
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// Check for dangerous patterns using regex
|
|
65
|
+
for (const pattern of ReadDataTool.DANGEROUS_PATTERNS) {
|
|
66
|
+
if (pattern.test(query)) {
|
|
67
|
+
return {
|
|
68
|
+
isValid: false,
|
|
69
|
+
error: 'Potentially malicious SQL pattern detected. Only simple SELECT queries are allowed.'
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Additional validation: Check for multiple statements
|
|
74
|
+
const statements = cleanQuery.split(';').filter(stmt => stmt.trim().length > 0);
|
|
75
|
+
if (statements.length > 1) {
|
|
76
|
+
return {
|
|
77
|
+
isValid: false,
|
|
78
|
+
error: 'Multiple SQL statements are not allowed. Use only a single SELECT statement.'
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
// Check for suspicious string patterns that might indicate obfuscation
|
|
82
|
+
if (query.includes('CHAR(') || query.includes('NCHAR(') || query.includes('ASCII(')) {
|
|
83
|
+
return {
|
|
84
|
+
isValid: false,
|
|
85
|
+
error: 'Character conversion functions are not allowed as they may be used for obfuscation.'
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
// Limit query length to prevent potential DoS
|
|
89
|
+
if (query.length > 10000) {
|
|
90
|
+
return {
|
|
91
|
+
isValid: false,
|
|
92
|
+
error: 'Query is too long. Maximum allowed length is 10,000 characters.'
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
return { isValid: true };
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Sanitizes the query result to prevent any potential security issues
|
|
99
|
+
* @param data The query result data
|
|
100
|
+
* @returns Sanitized data
|
|
101
|
+
*/
|
|
102
|
+
sanitizeResult(data) {
|
|
103
|
+
if (!Array.isArray(data)) {
|
|
104
|
+
return [];
|
|
105
|
+
}
|
|
106
|
+
// Limit the number of returned records to prevent memory issues
|
|
107
|
+
const maxRecords = 10000;
|
|
108
|
+
if (data.length > maxRecords) {
|
|
109
|
+
console.warn(`Query returned ${data.length} records, limiting to ${maxRecords}`);
|
|
110
|
+
return data.slice(0, maxRecords);
|
|
111
|
+
}
|
|
112
|
+
return data.map(record => {
|
|
113
|
+
if (typeof record === 'object' && record !== null) {
|
|
114
|
+
const sanitized = {};
|
|
115
|
+
for (const [key, value] of Object.entries(record)) {
|
|
116
|
+
// Sanitize column names (remove any suspicious characters)
|
|
117
|
+
const sanitizedKey = key.replace(/[^\w\s-_.]/g, '');
|
|
118
|
+
if (sanitizedKey !== key) {
|
|
119
|
+
console.warn(`Column name sanitized: ${key} -> ${sanitizedKey}`);
|
|
120
|
+
}
|
|
121
|
+
sanitized[sanitizedKey] = value;
|
|
122
|
+
}
|
|
123
|
+
return sanitized;
|
|
124
|
+
}
|
|
125
|
+
return record;
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Executes the validated SQL query
|
|
130
|
+
* @param params Query parameters
|
|
131
|
+
* @returns Query execution result
|
|
132
|
+
*/
|
|
133
|
+
async run(params) {
|
|
134
|
+
try {
|
|
135
|
+
const { query, databaseName } = params;
|
|
136
|
+
const { request, error } = await getSqlRequest(databaseName);
|
|
137
|
+
if (error) {
|
|
138
|
+
return { success: false, message: error, error: "INVALID_DATABASE" };
|
|
139
|
+
}
|
|
140
|
+
// Validate the query for security issues
|
|
141
|
+
const validation = this.validateQuery(query);
|
|
142
|
+
if (!validation.isValid) {
|
|
143
|
+
console.warn(`Security validation failed for query: ${query.substring(0, 100)}...`);
|
|
144
|
+
return {
|
|
145
|
+
success: false,
|
|
146
|
+
message: `Security validation failed: ${validation.error}`,
|
|
147
|
+
error: 'SECURITY_VALIDATION_FAILED'
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
// Log the query for audit purposes (in production, consider more secure logging)
|
|
151
|
+
console.log(`Executing validated SELECT query: ${query.substring(0, 200)}${query.length > 200 ? '...' : ''}`);
|
|
152
|
+
// Execute the query
|
|
153
|
+
const result = await request.query(query);
|
|
154
|
+
// Sanitize the result
|
|
155
|
+
const sanitizedData = this.sanitizeResult(result.recordset);
|
|
156
|
+
return {
|
|
157
|
+
success: true,
|
|
158
|
+
message: `Query executed successfully. Retrieved ${sanitizedData.length} record(s)${result.recordset.length !== sanitizedData.length
|
|
159
|
+
? ` (limited from ${result.recordset.length} total records)`
|
|
160
|
+
: ''}`,
|
|
161
|
+
data: sanitizedData,
|
|
162
|
+
recordCount: sanitizedData.length,
|
|
163
|
+
totalRecords: result.recordset.length
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
console.error("Error executing query:", error);
|
|
168
|
+
// Don't expose internal error details to prevent information leakage
|
|
169
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
170
|
+
const safeErrorMessage = errorMessage.includes('Invalid object name')
|
|
171
|
+
? errorMessage
|
|
172
|
+
: 'Database query execution failed';
|
|
173
|
+
return {
|
|
174
|
+
success: false,
|
|
175
|
+
message: `Failed to execute query: ${safeErrorMessage}`,
|
|
176
|
+
error: 'QUERY_EXECUTION_FAILED'
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// List of dangerous SQL keywords that should not be allowed
|
|
182
|
+
ReadDataTool.DANGEROUS_KEYWORDS = [
|
|
183
|
+
'DELETE', 'DROP', 'UPDATE', 'INSERT', 'ALTER', 'CREATE',
|
|
184
|
+
'TRUNCATE', 'EXEC', 'EXECUTE', 'MERGE', 'REPLACE',
|
|
185
|
+
'GRANT', 'REVOKE', 'COMMIT', 'ROLLBACK', 'TRANSACTION',
|
|
186
|
+
'BEGIN', 'DECLARE', 'SET', 'USE', 'BACKUP',
|
|
187
|
+
'RESTORE', 'KILL', 'SHUTDOWN', 'WAITFOR', 'OPENROWSET',
|
|
188
|
+
'OPENDATASOURCE', 'OPENQUERY', 'OPENXML', 'BULK', 'INTO'
|
|
189
|
+
];
|
|
190
|
+
// Regex patterns to detect common SQL injection techniques
|
|
191
|
+
ReadDataTool.DANGEROUS_PATTERNS = [
|
|
192
|
+
// SELECT INTO operations that create new tables
|
|
193
|
+
/SELECT\s+.*?\s+INTO\s+/i,
|
|
194
|
+
// Semicolon followed by dangerous keywords
|
|
195
|
+
/;\s*(DELETE|DROP|UPDATE|INSERT|ALTER|CREATE|TRUNCATE|EXEC|EXECUTE|MERGE|REPLACE|GRANT|REVOKE)/i,
|
|
196
|
+
// UNION injection attempts with dangerous keywords
|
|
197
|
+
/UNION\s+(?:ALL\s+)?SELECT.*?(DELETE|DROP|UPDATE|INSERT|ALTER|CREATE|TRUNCATE|EXEC|EXECUTE)/i,
|
|
198
|
+
// Comment-based injection attempts
|
|
199
|
+
/--.*?(DELETE|DROP|UPDATE|INSERT|ALTER|CREATE|TRUNCATE|EXEC|EXECUTE)/i,
|
|
200
|
+
/\/\*.*?(DELETE|DROP|UPDATE|INSERT|ALTER|CREATE|TRUNCATE|EXEC|EXECUTE).*?\*\//i,
|
|
201
|
+
// Stored procedure execution patterns
|
|
202
|
+
/EXEC\s*\(/i,
|
|
203
|
+
/EXECUTE\s*\(/i,
|
|
204
|
+
/sp_/i,
|
|
205
|
+
/xp_/i,
|
|
206
|
+
// Dynamic SQL construction
|
|
207
|
+
/EXEC\s*\(/i,
|
|
208
|
+
/EXECUTE\s*\(/i,
|
|
209
|
+
// Bulk operations
|
|
210
|
+
/BULK\s+INSERT/i,
|
|
211
|
+
/OPENROWSET/i,
|
|
212
|
+
/OPENDATASOURCE/i,
|
|
213
|
+
// System functions that could be dangerous
|
|
214
|
+
/@@/,
|
|
215
|
+
/SYSTEM_USER/i,
|
|
216
|
+
/USER_NAME/i,
|
|
217
|
+
/DB_NAME/i,
|
|
218
|
+
/HOST_NAME/i,
|
|
219
|
+
// Time delay attacks
|
|
220
|
+
/WAITFOR\s+DELAY/i,
|
|
221
|
+
/WAITFOR\s+TIME/i,
|
|
222
|
+
// Multiple statements (semicolon not at end)
|
|
223
|
+
/;\s*\w/,
|
|
224
|
+
// String concatenation that might hide malicious code
|
|
225
|
+
/\+\s*CHAR\s*\(/i,
|
|
226
|
+
/\+\s*NCHAR\s*\(/i,
|
|
227
|
+
/\+\s*ASCII\s*\(/i,
|
|
228
|
+
];
|
|
229
|
+
//# sourceMappingURL=ReadDataTool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReadDataTool.js","sourceRoot":"","sources":["../../src/tools/ReadDataTool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
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
|
+
}
|
|
17
|
+
//# sourceMappingURL=UpdateDataTool.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
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
|
+
}
|
|
66
|
+
//# sourceMappingURL=UpdateDataTool.js.map
|
|
@@ -0,0 +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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@eamonboyle/mssql-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for Microsoft SQL Server - query and manage your database with natural language in Cursor, Claude, and VS Code",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"mcp",
|
|
7
|
+
"mssql",
|
|
8
|
+
"sql-server",
|
|
9
|
+
"database",
|
|
10
|
+
"model-context-protocol",
|
|
11
|
+
"cursor",
|
|
12
|
+
"claude",
|
|
13
|
+
"vscode"
|
|
14
|
+
],
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/eamonboyle/mssql-mcp.git"
|
|
19
|
+
},
|
|
20
|
+
"homepage": "https://github.com/eamonboyle/mssql-mcp#readme",
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/eamonboyle/mssql-mcp/issues"
|
|
23
|
+
},
|
|
24
|
+
"main": "dist/index.js",
|
|
25
|
+
"bin": {
|
|
26
|
+
"mssql-mcp": "dist/index.js"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsc && shx chmod +x dist/*.js",
|
|
30
|
+
"prepare": "npm run build",
|
|
31
|
+
"watch": "tsc --watch",
|
|
32
|
+
"start": "node dist/index.js"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"dist"
|
|
36
|
+
],
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18.0.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/mssql": "^9.1.7",
|
|
42
|
+
"@types/node": "^16.7.13",
|
|
43
|
+
"shx": "^0.3.4",
|
|
44
|
+
"typescript": "^4.9.5"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
48
|
+
"dotenv": "^10.0.0",
|
|
49
|
+
"mssql": "^11.0.1"
|
|
50
|
+
},
|
|
51
|
+
"type": "module"
|
|
52
|
+
}
|