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/README.md +93 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +501 -0
- package/package.json +59 -0
package/README.md
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# MS SQL Server MCP Server
|
|
2
|
+
|
|
3
|
+
Model Context Protocol (MCP) server for Microsoft SQL Server. Designed for use in IDEs like Claude Desktop, Cursor, Windsurf, and VS Code.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🔗 **Database Connection Management**: Secure connection to MS SQL Server
|
|
8
|
+
- 📊 **SQL Query Execution**: Parameterized queries and DDL/DML operations
|
|
9
|
+
- 🗂️ **Schema Management**: Tables, views, stored procedures
|
|
10
|
+
- 📋 **Table Operations**: Structure inspection, data viewing, pagination
|
|
11
|
+
- ⚙️ **Stored Procedures**: Execute with parameters
|
|
12
|
+
- 🏢 **Database Listing**: All databases in the instance
|
|
13
|
+
- 🔒 **Security**: Environment variable support
|
|
14
|
+
|
|
15
|
+
## IDE Configuration
|
|
16
|
+
|
|
17
|
+
This MCP server can be used in IDEs like Claude Desktop, Cursor, Windsurf, and VS Code.
|
|
18
|
+
|
|
19
|
+
### Configuration Files
|
|
20
|
+
|
|
21
|
+
**For Claude Desktop**: `%APPDATA%\Claude\claude_desktop_config.json` (Windows) or `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS)
|
|
22
|
+
|
|
23
|
+
**For VS Code-based IDEs**: `.vscode/mcp.json`
|
|
24
|
+
|
|
25
|
+
### Basic Configuration
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"mcpServers": {
|
|
30
|
+
"mssql": {
|
|
31
|
+
"command": "npx",
|
|
32
|
+
"args": ["-y", "mssql-mcp@latest"],
|
|
33
|
+
"env": {
|
|
34
|
+
"DB_SERVER": "your-server.com",
|
|
35
|
+
"DB_DATABASE": "your-database",
|
|
36
|
+
"DB_USER": "your-username",
|
|
37
|
+
"DB_PASSWORD": "your-password",
|
|
38
|
+
"DB_PORT": "1433",
|
|
39
|
+
"DB_TRUST_SERVER_CERTIFICATE": "true"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
> **Note**: Use `"servers"` instead of `"mcpServers"` in VS Code-based IDEs.
|
|
47
|
+
|
|
48
|
+
### Platform Specific Settings
|
|
49
|
+
|
|
50
|
+
- **macOS/Linux**: Use the configuration above as is
|
|
51
|
+
- **Windows**: Use `"command": "cmd"` and `"args": ["/c", "npx", "-y", "mssql-mcp@latest"]`
|
|
52
|
+
- **WSL**: Use `"command": "wsl"` and `"args": ["npx", "-y", "mssql-mcp@latest"]`
|
|
53
|
+
|
|
54
|
+
## Environment Variables
|
|
55
|
+
|
|
56
|
+
You can use the following environment variables:
|
|
57
|
+
|
|
58
|
+
- `DB_SERVER`: SQL Server address
|
|
59
|
+
- `DB_DATABASE`: Database name
|
|
60
|
+
- `DB_USER`: Username (leave empty for Windows Authentication)
|
|
61
|
+
- `DB_PASSWORD`: Password
|
|
62
|
+
- `DB_PORT`: Port number (default: 1433)
|
|
63
|
+
- `DB_TRUST_SERVER_CERTIFICATE`: SSL certificate trust (true/false)
|
|
64
|
+
|
|
65
|
+
## Available Functions
|
|
66
|
+
|
|
67
|
+
This MCP server provides 9 database operations:
|
|
68
|
+
|
|
69
|
+
| Fonksiyon | Açıklama |
|
|
70
|
+
|-----------|----------|
|
|
71
|
+
| `connect_database` | SQL Server'a bağlantı kurar |
|
|
72
|
+
| `connection_status` | Bağlantı durumunu kontrol eder |
|
|
73
|
+
| `disconnect_database` | Bağlantıyı kapatır |
|
|
74
|
+
| `execute_query` | SQL sorgusu çalıştırır (SELECT, INSERT, UPDATE, DELETE) |
|
|
75
|
+
| `execute_procedure` | Stored procedure çalıştırır |
|
|
76
|
+
| `get_schema` | Veritabanı şemasını listeler (tablolar, views, procedures) |
|
|
77
|
+
| `describe_table` | Tablo yapısını detaylı gösterir |
|
|
78
|
+
| `list_databases` | Tüm veritabanlarını listeler |
|
|
79
|
+
| `get_table_data` | Tablo verilerini sayfalama ile getirir |
|
|
80
|
+
|
|
81
|
+
## Güvenlik Notları
|
|
82
|
+
|
|
83
|
+
- Hassas bilgileri (şifreler) environment variable'lar ile yönetin
|
|
84
|
+
- Üretim ortamında güçlü şifreler kullanın
|
|
85
|
+
- SSL/TLS bağlantısı için sertifikaları doğrulayın
|
|
86
|
+
- SQL injection koruması için parametreli sorgular kullanın
|
|
87
|
+
|
|
88
|
+
## GitHub Repository
|
|
89
|
+
|
|
90
|
+
Bu proje GitHub'da da mevcuttur:
|
|
91
|
+
- **Repository**: [BYMCS/mssql-mcp](https://github.com/BYMCS/mssql-mcp)
|
|
92
|
+
- **Issues**: Hata bildirimleri ve öneriler için
|
|
93
|
+
- **Releases**: Sürüm notları ve indirmeler
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import sql from "mssql";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import dotenv from "dotenv";
|
|
7
|
+
// Load environment variables
|
|
8
|
+
dotenv.config();
|
|
9
|
+
// Database connection configuration schema
|
|
10
|
+
const ConfigSchema = z.object({
|
|
11
|
+
server: z.string(),
|
|
12
|
+
database: z.string().optional(),
|
|
13
|
+
user: z.string().optional(),
|
|
14
|
+
password: z.string().optional(),
|
|
15
|
+
port: z.number().optional().default(1433),
|
|
16
|
+
trustServerCertificate: z.boolean().optional().default(true),
|
|
17
|
+
connectionTimeout: z.number().optional().default(30000),
|
|
18
|
+
requestTimeout: z.number().optional().default(30000),
|
|
19
|
+
});
|
|
20
|
+
class MSSQLMCPServer {
|
|
21
|
+
server;
|
|
22
|
+
pool = null;
|
|
23
|
+
config = null;
|
|
24
|
+
constructor() {
|
|
25
|
+
this.server = new McpServer({
|
|
26
|
+
name: "mssql-mcp-server",
|
|
27
|
+
version: "1.0.2",
|
|
28
|
+
});
|
|
29
|
+
this.setupTools();
|
|
30
|
+
this.setupResources();
|
|
31
|
+
}
|
|
32
|
+
setupTools() {
|
|
33
|
+
this.server.tool("connect_database", "Connect to MS SQL Server database", {
|
|
34
|
+
server: z.string().optional().describe("SQL Server instance name or IP address (uses DB_SERVER env var if not provided)"),
|
|
35
|
+
database: z.string().optional().describe("Database name (uses DB_DATABASE env var if not provided)"),
|
|
36
|
+
user: z.string().optional().describe("Username (uses DB_USER env var if not provided, leave empty for Windows auth)"),
|
|
37
|
+
password: z.string().optional().describe("Password (uses DB_PASSWORD env var if not provided)"),
|
|
38
|
+
port: z.number().optional().describe("Port number (uses DB_PORT env var or defaults to 1433)"),
|
|
39
|
+
trustServerCertificate: z.boolean().optional().describe("Trust server certificate (uses DB_TRUST_SERVER_CERTIFICATE env var or defaults to true)"),
|
|
40
|
+
}, async (args) => {
|
|
41
|
+
try {
|
|
42
|
+
// Use environment variables as defaults
|
|
43
|
+
const config = ConfigSchema.parse({
|
|
44
|
+
server: args.server || process.env.DB_SERVER,
|
|
45
|
+
database: args.database || process.env.DB_DATABASE,
|
|
46
|
+
user: args.user || process.env.DB_USER,
|
|
47
|
+
password: args.password || process.env.DB_PASSWORD,
|
|
48
|
+
port: args.port || (process.env.DB_PORT ? parseInt(process.env.DB_PORT) : 1433),
|
|
49
|
+
trustServerCertificate: args.trustServerCertificate ?? (process.env.DB_TRUST_SERVER_CERTIFICATE === 'true'),
|
|
50
|
+
connectionTimeout: process.env.DB_CONNECTION_TIMEOUT ? parseInt(process.env.DB_CONNECTION_TIMEOUT) : 30000,
|
|
51
|
+
requestTimeout: process.env.DB_REQUEST_TIMEOUT ? parseInt(process.env.DB_REQUEST_TIMEOUT) : 30000,
|
|
52
|
+
});
|
|
53
|
+
if (!config.server) {
|
|
54
|
+
throw new Error("Server is required. Provide it as parameter or set DB_SERVER environment variable.");
|
|
55
|
+
}
|
|
56
|
+
await this.connect(config);
|
|
57
|
+
return {
|
|
58
|
+
content: [
|
|
59
|
+
{
|
|
60
|
+
type: "text",
|
|
61
|
+
text: `Successfully connected to SQL Server: ${config.server}${config.database ? ` (Database: ${config.database})` : ""}`,
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
return {
|
|
68
|
+
content: [
|
|
69
|
+
{
|
|
70
|
+
type: "text",
|
|
71
|
+
text: `Failed to connect: ${error instanceof Error ? error.message : String(error)}`,
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
isError: true,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
// Tool: Execute SQL query
|
|
79
|
+
this.server.tool("execute_query", "Execute a SQL query against the connected database", {
|
|
80
|
+
query: z.string().describe("SQL query to execute"),
|
|
81
|
+
parameters: z.record(z.any()).optional().describe("Query parameters (key-value pairs)"),
|
|
82
|
+
}, async ({ query, parameters }) => {
|
|
83
|
+
try {
|
|
84
|
+
if (!this.pool) {
|
|
85
|
+
throw new Error("No database connection. Please connect first using connect_database tool.");
|
|
86
|
+
}
|
|
87
|
+
const request = this.pool.request();
|
|
88
|
+
// Add parameters if provided
|
|
89
|
+
if (parameters) {
|
|
90
|
+
for (const [key, value] of Object.entries(parameters)) {
|
|
91
|
+
request.input(key, value);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const result = await request.query(query);
|
|
95
|
+
return {
|
|
96
|
+
content: [
|
|
97
|
+
{
|
|
98
|
+
type: "text",
|
|
99
|
+
text: JSON.stringify({
|
|
100
|
+
recordset: result.recordset,
|
|
101
|
+
rowsAffected: result.rowsAffected,
|
|
102
|
+
output: result.output,
|
|
103
|
+
}, null, 2),
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
return {
|
|
110
|
+
content: [
|
|
111
|
+
{
|
|
112
|
+
type: "text",
|
|
113
|
+
text: `Query execution failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
isError: true,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
// Tool: Get database schema
|
|
121
|
+
this.server.tool("get_schema", "Get database schema information (tables, columns, etc.)", {
|
|
122
|
+
objectType: z.enum(["tables", "views", "procedures", "functions", "all"]).optional().default("tables"),
|
|
123
|
+
schemaName: z.string().optional().describe("Specific schema name to filter"),
|
|
124
|
+
}, async ({ objectType, schemaName }) => {
|
|
125
|
+
try {
|
|
126
|
+
if (!this.pool) {
|
|
127
|
+
throw new Error("No database connection. Please connect first.");
|
|
128
|
+
}
|
|
129
|
+
let query = "";
|
|
130
|
+
if (objectType === "tables" || objectType === "all") {
|
|
131
|
+
query += `
|
|
132
|
+
SELECT
|
|
133
|
+
TABLE_SCHEMA,
|
|
134
|
+
TABLE_NAME,
|
|
135
|
+
TABLE_TYPE,
|
|
136
|
+
'table' as OBJECT_TYPE
|
|
137
|
+
FROM INFORMATION_SCHEMA.TABLES
|
|
138
|
+
${schemaName ? `WHERE TABLE_SCHEMA = '${schemaName}'` : ""}
|
|
139
|
+
`;
|
|
140
|
+
}
|
|
141
|
+
if (objectType === "views" || objectType === "all") {
|
|
142
|
+
if (query)
|
|
143
|
+
query += " UNION ALL ";
|
|
144
|
+
query += `
|
|
145
|
+
SELECT
|
|
146
|
+
TABLE_SCHEMA,
|
|
147
|
+
TABLE_NAME,
|
|
148
|
+
'VIEW' as TABLE_TYPE,
|
|
149
|
+
'view' as OBJECT_TYPE
|
|
150
|
+
FROM INFORMATION_SCHEMA.VIEWS
|
|
151
|
+
${schemaName ? `WHERE TABLE_SCHEMA = '${schemaName}'` : ""}
|
|
152
|
+
`;
|
|
153
|
+
}
|
|
154
|
+
if (objectType === "procedures" || objectType === "all") {
|
|
155
|
+
if (query)
|
|
156
|
+
query += " UNION ALL ";
|
|
157
|
+
query += `
|
|
158
|
+
SELECT
|
|
159
|
+
ROUTINE_SCHEMA as TABLE_SCHEMA,
|
|
160
|
+
ROUTINE_NAME as TABLE_NAME,
|
|
161
|
+
'PROCEDURE' as TABLE_TYPE,
|
|
162
|
+
'procedure' as OBJECT_TYPE
|
|
163
|
+
FROM INFORMATION_SCHEMA.ROUTINES
|
|
164
|
+
WHERE ROUTINE_TYPE = 'PROCEDURE'
|
|
165
|
+
${schemaName ? `AND ROUTINE_SCHEMA = '${schemaName}'` : ""}
|
|
166
|
+
`;
|
|
167
|
+
}
|
|
168
|
+
if (objectType === "functions" || objectType === "all") {
|
|
169
|
+
if (query)
|
|
170
|
+
query += " UNION ALL ";
|
|
171
|
+
query += `
|
|
172
|
+
SELECT
|
|
173
|
+
ROUTINE_SCHEMA as TABLE_SCHEMA,
|
|
174
|
+
ROUTINE_NAME as TABLE_NAME,
|
|
175
|
+
'FUNCTION' as TABLE_TYPE,
|
|
176
|
+
'function' as OBJECT_TYPE
|
|
177
|
+
FROM INFORMATION_SCHEMA.ROUTINES
|
|
178
|
+
WHERE ROUTINE_TYPE = 'FUNCTION'
|
|
179
|
+
${schemaName ? `AND ROUTINE_SCHEMA = '${schemaName}'` : ""}
|
|
180
|
+
`;
|
|
181
|
+
}
|
|
182
|
+
query += " ORDER BY TABLE_SCHEMA, TABLE_NAME";
|
|
183
|
+
const result = await this.pool.request().query(query);
|
|
184
|
+
return {
|
|
185
|
+
content: [
|
|
186
|
+
{
|
|
187
|
+
type: "text",
|
|
188
|
+
text: JSON.stringify(result.recordset, null, 2),
|
|
189
|
+
},
|
|
190
|
+
],
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
return {
|
|
195
|
+
content: [
|
|
196
|
+
{
|
|
197
|
+
type: "text",
|
|
198
|
+
text: `Schema query failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
199
|
+
},
|
|
200
|
+
],
|
|
201
|
+
isError: true,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
// Tool: Get table structure
|
|
206
|
+
this.server.tool("describe_table", "Get detailed structure of a specific table", {
|
|
207
|
+
tableName: z.string().describe("Name of the table"),
|
|
208
|
+
schemaName: z.string().optional().default("dbo").describe("Schema name"),
|
|
209
|
+
}, async ({ tableName, schemaName }) => {
|
|
210
|
+
try {
|
|
211
|
+
if (!this.pool) {
|
|
212
|
+
throw new Error("No database connection. Please connect first.");
|
|
213
|
+
}
|
|
214
|
+
const query = `
|
|
215
|
+
SELECT
|
|
216
|
+
COLUMN_NAME,
|
|
217
|
+
DATA_TYPE,
|
|
218
|
+
CHARACTER_MAXIMUM_LENGTH,
|
|
219
|
+
NUMERIC_PRECISION,
|
|
220
|
+
NUMERIC_SCALE,
|
|
221
|
+
IS_NULLABLE,
|
|
222
|
+
COLUMN_DEFAULT,
|
|
223
|
+
ORDINAL_POSITION
|
|
224
|
+
FROM INFORMATION_SCHEMA.COLUMNS
|
|
225
|
+
WHERE TABLE_NAME = @tableName
|
|
226
|
+
AND TABLE_SCHEMA = @schemaName
|
|
227
|
+
ORDER BY ORDINAL_POSITION
|
|
228
|
+
`;
|
|
229
|
+
const result = await this.pool.request()
|
|
230
|
+
.input('tableName', sql.VarChar, tableName)
|
|
231
|
+
.input('schemaName', sql.VarChar, schemaName)
|
|
232
|
+
.query(query);
|
|
233
|
+
return {
|
|
234
|
+
content: [
|
|
235
|
+
{
|
|
236
|
+
type: "text",
|
|
237
|
+
text: JSON.stringify(result.recordset, null, 2),
|
|
238
|
+
},
|
|
239
|
+
],
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
return {
|
|
244
|
+
content: [
|
|
245
|
+
{
|
|
246
|
+
type: "text",
|
|
247
|
+
text: `Table description failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
248
|
+
},
|
|
249
|
+
],
|
|
250
|
+
isError: true,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
// Tool: Get connection status
|
|
255
|
+
this.server.tool("connection_status", "Check current database connection status", {}, async () => {
|
|
256
|
+
const isConnected = this.pool?.connected || false;
|
|
257
|
+
const status = {
|
|
258
|
+
connected: isConnected,
|
|
259
|
+
server: this.config?.server || "Not configured",
|
|
260
|
+
database: this.config?.database || "Not specified",
|
|
261
|
+
};
|
|
262
|
+
return {
|
|
263
|
+
content: [
|
|
264
|
+
{
|
|
265
|
+
type: "text",
|
|
266
|
+
text: JSON.stringify(status, null, 2),
|
|
267
|
+
},
|
|
268
|
+
],
|
|
269
|
+
};
|
|
270
|
+
});
|
|
271
|
+
// Tool: Disconnect from database
|
|
272
|
+
this.server.tool("disconnect_database", "Disconnect from the current database", {}, async () => {
|
|
273
|
+
try {
|
|
274
|
+
if (this.pool) {
|
|
275
|
+
await this.pool.close();
|
|
276
|
+
this.pool = null;
|
|
277
|
+
this.config = null;
|
|
278
|
+
}
|
|
279
|
+
return {
|
|
280
|
+
content: [
|
|
281
|
+
{
|
|
282
|
+
type: "text",
|
|
283
|
+
text: "Successfully disconnected from database",
|
|
284
|
+
},
|
|
285
|
+
],
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
catch (error) {
|
|
289
|
+
return {
|
|
290
|
+
content: [
|
|
291
|
+
{
|
|
292
|
+
type: "text",
|
|
293
|
+
text: `Disconnect failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
294
|
+
},
|
|
295
|
+
],
|
|
296
|
+
isError: true,
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
// Tool: Get table data with pagination
|
|
301
|
+
this.server.tool("get_table_data", "Get data from a specific table with optional filtering and pagination", {
|
|
302
|
+
tableName: z.string().describe("Name of the table"),
|
|
303
|
+
schemaName: z.string().optional().default("dbo").describe("Schema name"),
|
|
304
|
+
limit: z.number().optional().default(100).describe("Maximum number of rows to return"),
|
|
305
|
+
offset: z.number().optional().default(0).describe("Number of rows to skip"),
|
|
306
|
+
whereClause: z.string().optional().describe("WHERE clause (without the WHERE keyword)"),
|
|
307
|
+
orderBy: z.string().optional().describe("ORDER BY clause (without the ORDER BY keyword)"),
|
|
308
|
+
}, async ({ tableName, schemaName, limit, offset, whereClause, orderBy }) => {
|
|
309
|
+
try {
|
|
310
|
+
if (!this.pool) {
|
|
311
|
+
throw new Error("No database connection. Please connect first.");
|
|
312
|
+
}
|
|
313
|
+
let query = `SELECT * FROM [${schemaName}].[${tableName}]`;
|
|
314
|
+
if (whereClause) {
|
|
315
|
+
query += ` WHERE ${whereClause}`;
|
|
316
|
+
}
|
|
317
|
+
if (orderBy) {
|
|
318
|
+
query += ` ORDER BY ${orderBy}`;
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
// Default ordering for pagination
|
|
322
|
+
query += ` ORDER BY (SELECT NULL)`;
|
|
323
|
+
}
|
|
324
|
+
query += ` OFFSET ${offset} ROWS FETCH NEXT ${limit} ROWS ONLY`;
|
|
325
|
+
const result = await this.pool.request().query(query);
|
|
326
|
+
return {
|
|
327
|
+
content: [
|
|
328
|
+
{
|
|
329
|
+
type: "text",
|
|
330
|
+
text: JSON.stringify({
|
|
331
|
+
data: result.recordset,
|
|
332
|
+
rowCount: result.recordset.length,
|
|
333
|
+
offset: offset,
|
|
334
|
+
limit: limit,
|
|
335
|
+
}, null, 2),
|
|
336
|
+
},
|
|
337
|
+
],
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
catch (error) {
|
|
341
|
+
return {
|
|
342
|
+
content: [
|
|
343
|
+
{
|
|
344
|
+
type: "text",
|
|
345
|
+
text: `Get table data failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
346
|
+
},
|
|
347
|
+
],
|
|
348
|
+
isError: true,
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
// Tool: Execute stored procedure
|
|
353
|
+
this.server.tool("execute_procedure", "Execute a stored procedure with parameters", {
|
|
354
|
+
procedureName: z.string().describe("Name of the stored procedure"),
|
|
355
|
+
schemaName: z.string().optional().default("dbo").describe("Schema name"),
|
|
356
|
+
parameters: z.record(z.any()).optional().describe("Procedure parameters (key-value pairs)"),
|
|
357
|
+
}, async ({ procedureName, schemaName, parameters }) => {
|
|
358
|
+
try {
|
|
359
|
+
if (!this.pool) {
|
|
360
|
+
throw new Error("No database connection. Please connect first.");
|
|
361
|
+
}
|
|
362
|
+
const request = this.pool.request();
|
|
363
|
+
// Add parameters if provided
|
|
364
|
+
if (parameters) {
|
|
365
|
+
for (const [key, value] of Object.entries(parameters)) {
|
|
366
|
+
request.input(key, value);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
const result = await request.execute(`[${schemaName}].[${procedureName}]`);
|
|
370
|
+
return {
|
|
371
|
+
content: [
|
|
372
|
+
{
|
|
373
|
+
type: "text",
|
|
374
|
+
text: JSON.stringify({
|
|
375
|
+
recordsets: result.recordsets,
|
|
376
|
+
rowsAffected: result.rowsAffected,
|
|
377
|
+
output: result.output,
|
|
378
|
+
returnValue: result.returnValue,
|
|
379
|
+
}, null, 2),
|
|
380
|
+
},
|
|
381
|
+
],
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
catch (error) {
|
|
385
|
+
return {
|
|
386
|
+
content: [
|
|
387
|
+
{
|
|
388
|
+
type: "text",
|
|
389
|
+
text: `Procedure execution failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
390
|
+
},
|
|
391
|
+
],
|
|
392
|
+
isError: true,
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
// Tool: Get database list
|
|
397
|
+
this.server.tool("list_databases", "List all databases on the connected SQL Server instance", {}, async () => {
|
|
398
|
+
try {
|
|
399
|
+
if (!this.pool) {
|
|
400
|
+
throw new Error("No database connection. Please connect first.");
|
|
401
|
+
}
|
|
402
|
+
const query = `
|
|
403
|
+
SELECT
|
|
404
|
+
name,
|
|
405
|
+
database_id,
|
|
406
|
+
create_date,
|
|
407
|
+
collation_name,
|
|
408
|
+
state_desc,
|
|
409
|
+
user_access_desc,
|
|
410
|
+
is_read_only,
|
|
411
|
+
is_auto_close_on,
|
|
412
|
+
is_auto_shrink_on,
|
|
413
|
+
recovery_model_desc
|
|
414
|
+
FROM sys.databases
|
|
415
|
+
ORDER BY name
|
|
416
|
+
`;
|
|
417
|
+
const result = await this.pool.request().query(query);
|
|
418
|
+
return {
|
|
419
|
+
content: [
|
|
420
|
+
{
|
|
421
|
+
type: "text",
|
|
422
|
+
text: JSON.stringify(result.recordset, null, 2),
|
|
423
|
+
},
|
|
424
|
+
],
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
catch (error) {
|
|
428
|
+
return {
|
|
429
|
+
content: [
|
|
430
|
+
{
|
|
431
|
+
type: "text",
|
|
432
|
+
text: `List databases failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
433
|
+
},
|
|
434
|
+
],
|
|
435
|
+
isError: true,
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
setupResources() {
|
|
441
|
+
// Resource: Current connection info
|
|
442
|
+
this.server.resource("connection-info", "mssql://connection/info", async () => {
|
|
443
|
+
const info = {
|
|
444
|
+
connected: this.pool?.connected || false,
|
|
445
|
+
config: this.config ? {
|
|
446
|
+
server: this.config.server,
|
|
447
|
+
database: this.config.database,
|
|
448
|
+
port: this.config.port,
|
|
449
|
+
} : null,
|
|
450
|
+
};
|
|
451
|
+
return {
|
|
452
|
+
contents: [
|
|
453
|
+
{
|
|
454
|
+
uri: "mssql://connection/info",
|
|
455
|
+
text: JSON.stringify(info, null, 2),
|
|
456
|
+
mimeType: "application/json",
|
|
457
|
+
},
|
|
458
|
+
],
|
|
459
|
+
};
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
async connect(config) {
|
|
463
|
+
// Close existing connection if any
|
|
464
|
+
if (this.pool) {
|
|
465
|
+
await this.pool.close();
|
|
466
|
+
}
|
|
467
|
+
// Create new connection pool
|
|
468
|
+
this.pool = new sql.ConnectionPool({
|
|
469
|
+
server: config.server,
|
|
470
|
+
database: config.database,
|
|
471
|
+
user: config.user,
|
|
472
|
+
password: config.password,
|
|
473
|
+
port: config.port,
|
|
474
|
+
options: {
|
|
475
|
+
trustServerCertificate: config.trustServerCertificate,
|
|
476
|
+
enableArithAbort: true,
|
|
477
|
+
},
|
|
478
|
+
connectionTimeout: config.connectionTimeout,
|
|
479
|
+
requestTimeout: config.requestTimeout,
|
|
480
|
+
});
|
|
481
|
+
await this.pool.connect();
|
|
482
|
+
this.config = config;
|
|
483
|
+
}
|
|
484
|
+
async run() {
|
|
485
|
+
const transport = new StdioServerTransport();
|
|
486
|
+
await this.server.connect(transport);
|
|
487
|
+
// Handle cleanup on exit
|
|
488
|
+
process.on('SIGINT', async () => {
|
|
489
|
+
if (this.pool) {
|
|
490
|
+
await this.pool.close();
|
|
491
|
+
}
|
|
492
|
+
process.exit(0);
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
// Start the server
|
|
497
|
+
const server = new MSSQLMCPServer();
|
|
498
|
+
server.run().catch((error) => {
|
|
499
|
+
console.error("Server failed to start:", error);
|
|
500
|
+
process.exit(1);
|
|
501
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mssql-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP Server for MS SQL Server integration with Claude Desktop, Cursor, Windsurf and VS Code",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"mssql-mcp-server": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist/**/*",
|
|
12
|
+
"README.md",
|
|
13
|
+
"package.json"
|
|
14
|
+
],
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/BYMCS/mssql-mcp.git"
|
|
18
|
+
},
|
|
19
|
+
"homepage": "https://github.com/BYMCS/mssql-mcp#readme",
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/BYMCS/mssql-mcp/issues"
|
|
22
|
+
},
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=18.0.0"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsc",
|
|
28
|
+
"dev": "tsc --watch",
|
|
29
|
+
"start": "node dist/index.js",
|
|
30
|
+
"clean": "rimraf dist",
|
|
31
|
+
"prepublishOnly": "npm run clean && npm run build",
|
|
32
|
+
"test": "echo \"No tests specified\" && exit 0"
|
|
33
|
+
},
|
|
34
|
+
"keywords": [
|
|
35
|
+
"mcp",
|
|
36
|
+
"mssql",
|
|
37
|
+
"sql-server",
|
|
38
|
+
"database",
|
|
39
|
+
"claude",
|
|
40
|
+
"cursor",
|
|
41
|
+
"windsurf",
|
|
42
|
+
"vscode",
|
|
43
|
+
"model-context-protocol"
|
|
44
|
+
],
|
|
45
|
+
"author": "BYMCS <hello@bymcs.com>",
|
|
46
|
+
"license": "MIT",
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
49
|
+
"mssql": "^11.0.1",
|
|
50
|
+
"zod": "^3.22.4",
|
|
51
|
+
"dotenv": "^16.3.1"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@types/node": "^20.0.0",
|
|
55
|
+
"@types/mssql": "^9.1.5",
|
|
56
|
+
"typescript": "^5.3.0",
|
|
57
|
+
"rimraf": "^5.0.0"
|
|
58
|
+
}
|
|
59
|
+
}
|