mssql-mcp 2.0.3 → 2.1.1
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 +79 -46
- package/dist/index.js +75 -33
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# MS SQL Server MCP Server v2.
|
|
1
|
+
# MS SQL Server MCP Server v2.1.1
|
|
2
2
|
|
|
3
|
-
🚀 **
|
|
3
|
+
🚀 **Model Context Protocol (MCP) server** for Microsoft SQL Server - compatible with Claude Desktop, Cursor, Windsurf and VS Code.
|
|
4
4
|
|
|
5
5
|
## 🚀 Quick Start
|
|
6
6
|
|
|
@@ -24,6 +24,7 @@ npm install -g mssql-mcp
|
|
|
24
24
|
"DB_DATABASE": "your-database",
|
|
25
25
|
"DB_USER": "your-username",
|
|
26
26
|
"DB_PASSWORD": "your-password",
|
|
27
|
+
"DB_ENCRYPT": "true",
|
|
27
28
|
"DB_TRUST_SERVER_CERTIFICATE": "true"
|
|
28
29
|
}
|
|
29
30
|
}
|
|
@@ -43,6 +44,7 @@ npm install -g mssql-mcp
|
|
|
43
44
|
"DB_DATABASE": "your-database",
|
|
44
45
|
"DB_USER": "your-username",
|
|
45
46
|
"DB_PASSWORD": "your-password",
|
|
47
|
+
"DB_ENCRYPT": "true",
|
|
46
48
|
"DB_TRUST_SERVER_CERTIFICATE": "true"
|
|
47
49
|
}
|
|
48
50
|
}
|
|
@@ -50,77 +52,108 @@ npm install -g mssql-mcp
|
|
|
50
52
|
}
|
|
51
53
|
```
|
|
52
54
|
|
|
53
|
-
> Replace with your actual database credentials.
|
|
55
|
+
> Replace with your actual database credentials.
|
|
54
56
|
|
|
55
57
|
## 🛠️ Available Tools
|
|
56
58
|
|
|
57
59
|
| Tool | Description |
|
|
58
60
|
|------|-------------|
|
|
61
|
+
| `connect_database` | Connect to database using environment variables |
|
|
62
|
+
| `disconnect_database` | Close current database connection |
|
|
63
|
+
| `connection_status` | Check connection state with pool info |
|
|
59
64
|
| `execute_query` | Execute any SQL query with parameters |
|
|
60
65
|
| `get_schema` | List database objects (tables, views, procedures) |
|
|
61
66
|
| `describe_table` | Get detailed table structure |
|
|
62
67
|
| `get_table_data` | Retrieve data with pagination |
|
|
63
68
|
| `execute_procedure` | Execute stored procedures |
|
|
64
69
|
| `list_databases` | List all databases |
|
|
65
|
-
| `connection_status` | Check connection state |
|
|
66
|
-
| `connect_database` | Manual connection (rarely needed) |
|
|
67
|
-
| `disconnect_database` | Close connection |
|
|
68
|
-
| `clear_cache` | Clear query cache |
|
|
69
|
-
|
|
70
|
-
All tools auto-connect using environment variables.
|
|
71
70
|
|
|
72
71
|
## 🔧 Environment Variables
|
|
73
72
|
|
|
74
|
-
| Variable | Required | Default |
|
|
75
|
-
|
|
76
|
-
| `DB_SERVER` | ✅ | - |
|
|
77
|
-
| `DB_DATABASE` |
|
|
78
|
-
| `DB_USER` |
|
|
79
|
-
| `DB_PASSWORD` |
|
|
80
|
-
| `DB_PORT` | ❌ | 1433 |
|
|
81
|
-
| `
|
|
82
|
-
| `
|
|
83
|
-
| `
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
73
|
+
| Variable | Required | Default | Description |
|
|
74
|
+
|----------|----------|---------|-------------|
|
|
75
|
+
| `DB_SERVER` | ✅ | - | SQL Server hostname |
|
|
76
|
+
| `DB_DATABASE` | ❌ | - | Database name |
|
|
77
|
+
| `DB_USER` | ❌ | - | Username |
|
|
78
|
+
| `DB_PASSWORD` | ❌ | - | Password |
|
|
79
|
+
| `DB_PORT` | ❌ | 1433 | SQL Server port |
|
|
80
|
+
| `DB_ENCRYPT` | ❌ | true | Enable TLS encryption (required for Azure SQL) |
|
|
81
|
+
| `DB_TRUST_SERVER_CERTIFICATE` | ❌ | false | Trust self-signed certificates |
|
|
82
|
+
| `DB_CONNECTION_TIMEOUT` | ❌ | 30000 | Connection timeout (ms) |
|
|
83
|
+
| `DB_REQUEST_TIMEOUT` | ❌ | 30000 | Request timeout (ms) |
|
|
84
|
+
|
|
85
|
+
### Azure SQL Configuration
|
|
86
|
+
For Azure SQL Database, use these settings:
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"DB_ENCRYPT": "true",
|
|
90
|
+
"DB_TRUST_SERVER_CERTIFICATE": "false"
|
|
91
|
+
}
|
|
92
|
+
```
|
|
88
93
|
|
|
89
|
-
|
|
94
|
+
### Local SQL Server (Self-signed cert)
|
|
95
|
+
For local development with self-signed certificates:
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"DB_ENCRYPT": "true",
|
|
99
|
+
"DB_TRUST_SERVER_CERTIFICATE": "true"
|
|
100
|
+
}
|
|
101
|
+
```
|
|
90
102
|
|
|
91
103
|
## 🏆 Features
|
|
92
104
|
|
|
93
|
-
- ✅ **
|
|
105
|
+
- ✅ **MCP SDK 1.25.1**: Latest Model Context Protocol SDK
|
|
106
|
+
- ✅ **Azure SQL Compatible**: TLS encryption enabled by default
|
|
94
107
|
- ✅ **Complete SQL Support**: All database operations
|
|
95
|
-
- ✅ **
|
|
96
|
-
- ✅ **
|
|
108
|
+
- ✅ **Parameterized Queries**: SQL injection protection
|
|
109
|
+
- ✅ **Connection Pooling**: Efficient resource management
|
|
97
110
|
- ✅ **Performance Monitoring**: Execution time tracking
|
|
98
|
-
|
|
99
|
-
|
|
111
|
+
|
|
112
|
+
## 📋 Usage Examples
|
|
113
|
+
|
|
114
|
+
### Connect to Database
|
|
115
|
+
```
|
|
116
|
+
Use the connect_database tool to establish a connection.
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Execute a Query
|
|
120
|
+
```sql
|
|
121
|
+
SELECT TOP 10 * FROM Customers WHERE Country = @country
|
|
122
|
+
-- With parameters: { "country": "USA" }
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Get Table Schema
|
|
126
|
+
```
|
|
127
|
+
Use describe_table with tableName: "Customers" to see column details.
|
|
128
|
+
```
|
|
100
129
|
|
|
101
130
|
## 🔍 Troubleshooting
|
|
102
131
|
|
|
103
|
-
**❌
|
|
104
|
-
-
|
|
105
|
-
-
|
|
106
|
-
-
|
|
132
|
+
**❌ Connection failed**
|
|
133
|
+
- Verify all required environment variables are set
|
|
134
|
+
- Check server accessibility and credentials
|
|
135
|
+
- Ensure network connectivity to SQL Server
|
|
107
136
|
|
|
108
|
-
**❌
|
|
109
|
-
-
|
|
110
|
-
-
|
|
137
|
+
**❌ SSL/Certificate errors**
|
|
138
|
+
- For Azure SQL: Set `DB_ENCRYPT=true` (default)
|
|
139
|
+
- For self-signed certs: Set `DB_TRUST_SERVER_CERTIFICATE=true`
|
|
140
|
+
- For local dev without encryption: Set `DB_ENCRYPT=false`
|
|
111
141
|
|
|
112
142
|
## 📋 Version History
|
|
113
143
|
|
|
114
|
-
### v2.
|
|
115
|
-
- ✅
|
|
116
|
-
- ✅
|
|
117
|
-
- ✅
|
|
144
|
+
### v2.1.1 - Latest
|
|
145
|
+
- ✅ Added `DB_ENCRYPT` environment variable (Issue #1)
|
|
146
|
+
- ✅ Azure SQL Database compatibility improved
|
|
147
|
+
- ✅ Encryption enabled by default (`DB_ENCRYPT=true`)
|
|
148
|
+
- ✅ Fixed `DB_TRUST_SERVER_CERTIFICATE` default to `false`
|
|
149
|
+
|
|
150
|
+
### v2.1.0
|
|
151
|
+
- ✅ Updated to MCP SDK 1.25.1
|
|
152
|
+
- ✅ Migrated to `registerTool()` / `registerResource()` API
|
|
153
|
+
- ✅ Added tool titles for better UI display
|
|
118
154
|
|
|
119
|
-
### v2.0.
|
|
120
|
-
- ✅
|
|
121
|
-
- ✅ All console output moved to stderr
|
|
122
|
-
- ✅ Clean, professional log messages
|
|
123
|
-
- ✅ Version bump to 2.0.2
|
|
155
|
+
### v2.0.3
|
|
156
|
+
- ✅ Documentation improvements
|
|
124
157
|
|
|
125
158
|
## 📄 License
|
|
126
159
|
|
|
@@ -133,4 +166,4 @@ MIT License
|
|
|
133
166
|
|
|
134
167
|
---
|
|
135
168
|
|
|
136
|
-
**🎉 v2.
|
|
169
|
+
**🎉 v2.1.1: Azure SQL compatibility with DB_ENCRYPT support**
|
package/dist/index.js
CHANGED
|
@@ -13,7 +13,8 @@ const ConfigSchema = z.object({
|
|
|
13
13
|
user: z.string().optional(),
|
|
14
14
|
password: z.string().optional(),
|
|
15
15
|
port: z.number().int().min(1).max(65535).optional().default(1433),
|
|
16
|
-
|
|
16
|
+
encrypt: z.boolean().optional().default(true),
|
|
17
|
+
trustServerCertificate: z.boolean().optional().default(false),
|
|
17
18
|
connectionTimeout: z.number().int().min(1000).max(60000).optional().default(30000),
|
|
18
19
|
requestTimeout: z.number().int().min(1000).max(300000).optional().default(30000),
|
|
19
20
|
});
|
|
@@ -24,16 +25,20 @@ class MSSQLMCPServer {
|
|
|
24
25
|
constructor() {
|
|
25
26
|
this.server = new McpServer({
|
|
26
27
|
name: "mssql-mcp-server",
|
|
27
|
-
version: "2.
|
|
28
|
+
version: "2.1.1",
|
|
28
29
|
});
|
|
29
30
|
this.setupTools();
|
|
30
31
|
this.setupResources();
|
|
31
32
|
}
|
|
32
33
|
setupTools() {
|
|
33
34
|
// Tool: Connect to database with enhanced security validation (only uses environment variables)
|
|
34
|
-
this.server.
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
this.server.registerTool("connect_database", {
|
|
36
|
+
title: "Connect Database",
|
|
37
|
+
description: "Connect to MS SQL Server database with security validation (uses only environment variables for security)",
|
|
38
|
+
inputSchema: {
|
|
39
|
+
// No parameters - only environment variables will be used for security
|
|
40
|
+
},
|
|
41
|
+
}, async () => {
|
|
37
42
|
try {
|
|
38
43
|
// SECURITY: Only use environment variables, ignore all user parameters
|
|
39
44
|
const config = ConfigSchema.parse({
|
|
@@ -42,6 +47,7 @@ class MSSQLMCPServer {
|
|
|
42
47
|
user: process.env.DB_USER,
|
|
43
48
|
password: process.env.DB_PASSWORD,
|
|
44
49
|
port: process.env.DB_PORT ? parseInt(process.env.DB_PORT) : 1433,
|
|
50
|
+
encrypt: process.env.DB_ENCRYPT !== 'false', // Default true for Azure SQL compatibility
|
|
45
51
|
trustServerCertificate: process.env.DB_TRUST_SERVER_CERTIFICATE === 'true',
|
|
46
52
|
connectionTimeout: process.env.DB_CONNECTION_TIMEOUT ? parseInt(process.env.DB_CONNECTION_TIMEOUT) : 30000,
|
|
47
53
|
requestTimeout: process.env.DB_REQUEST_TIMEOUT ? parseInt(process.env.DB_REQUEST_TIMEOUT) : 30000,
|
|
@@ -74,9 +80,13 @@ class MSSQLMCPServer {
|
|
|
74
80
|
}
|
|
75
81
|
});
|
|
76
82
|
// Tool: Execute SQL query with enhanced security
|
|
77
|
-
this.server.
|
|
78
|
-
|
|
79
|
-
|
|
83
|
+
this.server.registerTool("execute_query", {
|
|
84
|
+
title: "Execute Query",
|
|
85
|
+
description: "Execute a SQL query against the connected database with security validation",
|
|
86
|
+
inputSchema: {
|
|
87
|
+
query: z.string().min(1, "Query cannot be empty").describe("SQL query to execute"),
|
|
88
|
+
parameters: z.record(z.any()).optional().describe("Query parameters (key-value pairs) - always use parameters for user input"),
|
|
89
|
+
},
|
|
80
90
|
}, async ({ query, parameters }) => {
|
|
81
91
|
try {
|
|
82
92
|
if (!this.pool) {
|
|
@@ -122,9 +132,13 @@ class MSSQLMCPServer {
|
|
|
122
132
|
}
|
|
123
133
|
});
|
|
124
134
|
// Tool: Get database schema
|
|
125
|
-
this.server.
|
|
126
|
-
|
|
127
|
-
|
|
135
|
+
this.server.registerTool("get_schema", {
|
|
136
|
+
title: "Get Schema",
|
|
137
|
+
description: "Get database schema information (tables, columns, etc.)",
|
|
138
|
+
inputSchema: {
|
|
139
|
+
objectType: z.enum(["tables", "views", "procedures", "functions", "all"]).optional().default("tables"),
|
|
140
|
+
schemaName: z.string().optional().describe("Specific schema name to filter"),
|
|
141
|
+
},
|
|
128
142
|
}, async ({ objectType, schemaName }) => {
|
|
129
143
|
try {
|
|
130
144
|
if (!this.pool) {
|
|
@@ -207,9 +221,13 @@ class MSSQLMCPServer {
|
|
|
207
221
|
}
|
|
208
222
|
});
|
|
209
223
|
// Tool: Get table structure
|
|
210
|
-
this.server.
|
|
211
|
-
|
|
212
|
-
|
|
224
|
+
this.server.registerTool("describe_table", {
|
|
225
|
+
title: "Describe Table",
|
|
226
|
+
description: "Get detailed structure of a specific table",
|
|
227
|
+
inputSchema: {
|
|
228
|
+
tableName: z.string().describe("Name of the table"),
|
|
229
|
+
schemaName: z.string().optional().default("dbo").describe("Schema name"),
|
|
230
|
+
},
|
|
213
231
|
}, async ({ tableName, schemaName }) => {
|
|
214
232
|
try {
|
|
215
233
|
if (!this.pool) {
|
|
@@ -256,7 +274,11 @@ class MSSQLMCPServer {
|
|
|
256
274
|
}
|
|
257
275
|
});
|
|
258
276
|
// Tool: Get enhanced connection status
|
|
259
|
-
this.server.
|
|
277
|
+
this.server.registerTool("connection_status", {
|
|
278
|
+
title: "Connection Status",
|
|
279
|
+
description: "Check current database connection status with detailed information",
|
|
280
|
+
inputSchema: {},
|
|
281
|
+
}, async () => {
|
|
260
282
|
const isConnected = this.pool?.connected || false;
|
|
261
283
|
const status = {
|
|
262
284
|
connected: isConnected,
|
|
@@ -284,7 +306,11 @@ class MSSQLMCPServer {
|
|
|
284
306
|
};
|
|
285
307
|
});
|
|
286
308
|
// Tool: Disconnect from database
|
|
287
|
-
this.server.
|
|
309
|
+
this.server.registerTool("disconnect_database", {
|
|
310
|
+
title: "Disconnect Database",
|
|
311
|
+
description: "Disconnect from the current database",
|
|
312
|
+
inputSchema: {},
|
|
313
|
+
}, async () => {
|
|
288
314
|
try {
|
|
289
315
|
if (this.pool) {
|
|
290
316
|
await this.pool.close();
|
|
@@ -313,14 +339,18 @@ class MSSQLMCPServer {
|
|
|
313
339
|
}
|
|
314
340
|
});
|
|
315
341
|
// Tool: Get table data with enhanced security and validation
|
|
316
|
-
this.server.
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
342
|
+
this.server.registerTool("get_table_data", {
|
|
343
|
+
title: "Get Table Data",
|
|
344
|
+
description: "Get data from a specific table with optional filtering, pagination and input validation",
|
|
345
|
+
inputSchema: {
|
|
346
|
+
tableName: z.string().min(1).regex(/^[a-zA-Z0-9_]+$/, "Table name can only contain letters, numbers, and underscores").describe("Name of the table"),
|
|
347
|
+
schemaName: z.string().regex(/^[a-zA-Z0-9_]+$/, "Schema name can only contain letters, numbers, and underscores").optional().default("dbo").describe("Schema name"),
|
|
348
|
+
limit: z.number().int().min(1).max(10000).optional().default(100).describe("Maximum number of rows to return (1-10000)"),
|
|
349
|
+
offset: z.number().int().min(0).optional().default(0).describe("Number of rows to skip"),
|
|
350
|
+
whereClause: z.string().optional().describe("WHERE clause (without the WHERE keyword) - use parameters for values"),
|
|
351
|
+
orderBy: z.string().optional().describe("ORDER BY clause (without the ORDER BY keyword)"),
|
|
352
|
+
parameters: z.record(z.any()).optional().describe("Parameters for WHERE clause"),
|
|
353
|
+
},
|
|
324
354
|
}, async ({ tableName, schemaName, limit, offset, whereClause, orderBy, parameters }) => {
|
|
325
355
|
try {
|
|
326
356
|
if (!this.pool) {
|
|
@@ -349,7 +379,7 @@ class MSSQLMCPServer {
|
|
|
349
379
|
if (orderBy) {
|
|
350
380
|
// Validate ORDER BY clause for basic security
|
|
351
381
|
// Allow dotted identifiers, bracketed identifiers, commas, spaces and optional ASC/DESC per column
|
|
352
|
-
const orderByPattern = /^([\[\]a-zA-Z0-9_.]+(\s+(ASC|DESC))?)(\s*,\s*[\[\]a-zA-Z0-9_.]+(\s+(ASC|DESC))?)*$/i;
|
|
382
|
+
const orderByPattern = /^([\[\]a-zA-Z0-9_.]+(\s+(ASC|DESC))?)((\s*,\s*)[\[\]a-zA-Z0-9_.]+(\s+(ASC|DESC))?)*$/i;
|
|
353
383
|
if (!orderByPattern.test(orderBy)) {
|
|
354
384
|
throw new Error("Invalid ORDER BY clause. Only column names, commas, spaces, ASC, and DESC are allowed.");
|
|
355
385
|
}
|
|
@@ -398,10 +428,14 @@ class MSSQLMCPServer {
|
|
|
398
428
|
}
|
|
399
429
|
});
|
|
400
430
|
// Tool: Execute stored procedure
|
|
401
|
-
this.server.
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
431
|
+
this.server.registerTool("execute_procedure", {
|
|
432
|
+
title: "Execute Procedure",
|
|
433
|
+
description: "Execute a stored procedure with parameters",
|
|
434
|
+
inputSchema: {
|
|
435
|
+
procedureName: z.string().describe("Name of the stored procedure"),
|
|
436
|
+
schemaName: z.string().optional().default("dbo").describe("Schema name"),
|
|
437
|
+
parameters: z.record(z.any()).optional().describe("Procedure parameters (key-value pairs)"),
|
|
438
|
+
},
|
|
405
439
|
}, async ({ procedureName, schemaName, parameters }) => {
|
|
406
440
|
try {
|
|
407
441
|
if (!this.pool) {
|
|
@@ -442,7 +476,11 @@ class MSSQLMCPServer {
|
|
|
442
476
|
}
|
|
443
477
|
});
|
|
444
478
|
// Tool: Get database list
|
|
445
|
-
this.server.
|
|
479
|
+
this.server.registerTool("list_databases", {
|
|
480
|
+
title: "List Databases",
|
|
481
|
+
description: "List all databases on the connected SQL Server instance",
|
|
482
|
+
inputSchema: {},
|
|
483
|
+
}, async () => {
|
|
446
484
|
try {
|
|
447
485
|
if (!this.pool) {
|
|
448
486
|
throw new Error("No database connection. Please connect first.");
|
|
@@ -487,7 +525,11 @@ class MSSQLMCPServer {
|
|
|
487
525
|
}
|
|
488
526
|
setupResources() {
|
|
489
527
|
// Resource: Current connection info
|
|
490
|
-
this.server.
|
|
528
|
+
this.server.registerResource("connection-info", "mssql://connection/info", {
|
|
529
|
+
title: "Connection Info",
|
|
530
|
+
description: "Current MSSQL connection information",
|
|
531
|
+
mimeType: "application/json",
|
|
532
|
+
}, async () => {
|
|
491
533
|
const info = {
|
|
492
534
|
connected: this.pool?.connected || false,
|
|
493
535
|
config: this.config ? {
|
|
@@ -523,9 +565,9 @@ class MSSQLMCPServer {
|
|
|
523
565
|
password: config.password,
|
|
524
566
|
port: config.port,
|
|
525
567
|
options: {
|
|
568
|
+
encrypt: config.encrypt,
|
|
526
569
|
trustServerCertificate: config.trustServerCertificate,
|
|
527
570
|
enableArithAbort: true,
|
|
528
|
-
encrypt: !config.trustServerCertificate, // Enable encryption when not trusting server certificate
|
|
529
571
|
},
|
|
530
572
|
connectionTimeout: config.connectionTimeout,
|
|
531
573
|
requestTimeout: config.requestTimeout,
|
|
@@ -593,7 +635,7 @@ class MSSQLMCPServer {
|
|
|
593
635
|
console.error('Unhandled Rejection:', reason);
|
|
594
636
|
shutdown('unhandledRejection');
|
|
595
637
|
});
|
|
596
|
-
console.error("MSSQL MCP Server v2.
|
|
638
|
+
console.error("MSSQL MCP Server v2.1.1 starting...");
|
|
597
639
|
await this.server.connect(transport);
|
|
598
640
|
console.error("Server ready");
|
|
599
641
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mssql-mcp",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "MCP Server for MS SQL Server integration with Claude Desktop, Cursor, Windsurf and VS Code",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -45,10 +45,10 @@
|
|
|
45
45
|
"author": "BYMCS <hello@bymcs.com>",
|
|
46
46
|
"license": "MIT",
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
48
|
+
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
49
49
|
"dotenv": "^16.3.1",
|
|
50
50
|
"mssql": "^11.0.1",
|
|
51
|
-
"zod": "^3.
|
|
51
|
+
"zod": "^3.25.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@types/mssql": "^9.1.7",
|
|
@@ -56,4 +56,4 @@
|
|
|
56
56
|
"rimraf": "^5.0.0",
|
|
57
57
|
"typescript": "^5.3.0"
|
|
58
58
|
}
|
|
59
|
-
}
|
|
59
|
+
}
|