mssql-mcp 2.0.3 → 2.3.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 (60) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +168 -63
  3. package/dist/src/config.d.ts +39 -0
  4. package/dist/src/config.js +37 -0
  5. package/dist/src/constants.d.ts +15 -0
  6. package/dist/src/constants.js +15 -0
  7. package/dist/src/db/connection.d.ts +8 -0
  8. package/dist/src/db/connection.js +80 -0
  9. package/dist/src/db/query-builders.d.ts +3 -0
  10. package/dist/src/db/query-builders.js +58 -0
  11. package/dist/src/db/validators.d.ts +5 -0
  12. package/dist/src/db/validators.js +25 -0
  13. package/dist/src/index.js +26 -0
  14. package/dist/src/resources/connection.d.ts +2 -0
  15. package/dist/src/resources/connection.js +19 -0
  16. package/dist/src/resources/metadata.d.ts +2 -0
  17. package/dist/src/resources/metadata.js +58 -0
  18. package/dist/src/schemas/outputs.d.ts +153 -0
  19. package/dist/src/schemas/outputs.js +54 -0
  20. package/dist/src/server.d.ts +2 -0
  21. package/dist/src/server.js +27 -0
  22. package/dist/src/tools/connect.d.ts +2 -0
  23. package/dist/src/tools/connect.js +45 -0
  24. package/dist/src/tools/databases.d.ts +2 -0
  25. package/dist/src/tools/databases.js +53 -0
  26. package/dist/src/tools/procedure.d.ts +2 -0
  27. package/dist/src/tools/procedure.js +106 -0
  28. package/dist/src/tools/query.d.ts +2 -0
  29. package/dist/src/tools/query.js +92 -0
  30. package/dist/src/tools/schema.d.ts +2 -0
  31. package/dist/src/tools/schema.js +96 -0
  32. package/dist/src/tools/status.d.ts +2 -0
  33. package/dist/src/tools/status.js +17 -0
  34. package/dist/src/tools/table.d.ts +2 -0
  35. package/dist/src/tools/table.js +261 -0
  36. package/dist/src/transports/http.d.ts +3 -0
  37. package/dist/src/transports/http.js +54 -0
  38. package/dist/src/transports/stdio.d.ts +2 -0
  39. package/dist/src/transports/stdio.js +23 -0
  40. package/dist/src/types.d.ts +37 -0
  41. package/dist/src/types.js +1 -0
  42. package/dist/src/utils/errors.d.ts +19 -0
  43. package/dist/src/utils/errors.js +29 -0
  44. package/dist/src/utils/format.d.ts +6 -0
  45. package/dist/src/utils/format.js +27 -0
  46. package/dist/src/utils/markdown.d.ts +3 -0
  47. package/dist/src/utils/markdown.js +33 -0
  48. package/dist/src/utils/pagination.d.ts +3 -0
  49. package/dist/src/utils/pagination.js +18 -0
  50. package/dist/tests/unit/markdown.test.d.ts +1 -0
  51. package/dist/tests/unit/markdown.test.js +70 -0
  52. package/dist/tests/unit/query-builders.test.d.ts +1 -0
  53. package/dist/tests/unit/query-builders.test.js +63 -0
  54. package/dist/tests/unit/tool-contracts.test.d.ts +1 -0
  55. package/dist/tests/unit/tool-contracts.test.js +62 -0
  56. package/dist/tests/unit/validators.test.d.ts +1 -0
  57. package/dist/tests/unit/validators.test.js +51 -0
  58. package/package.json +12 -8
  59. package/dist/index.js +0 -606
  60. /package/dist/{index.d.ts → src/index.d.ts} +0 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-2025 BYMCS
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,6 +1,17 @@
1
- # MS SQL Server MCP Server v2.0.3
1
+ # MS SQL Server MCP Server v2.3.0
2
2
 
3
- 🚀 **Smart Trust-Based Model Context Protocol (MCP) server** for Microsoft SQL Server with intelligent auto-connection and AI-friendly design.
3
+ 🚀 **Model Context Protocol (MCP) server** for Microsoft SQL Server - compatible with Claude Desktop, Cursor, Windsurf and VS Code.
4
+
5
+ [![CI](https://github.com/BYMCS/mssql-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/BYMCS/mssql-mcp/actions/workflows/ci.yml)
6
+ [![npm version](https://img.shields.io/npm/v/mssql-mcp.svg)](https://www.npmjs.com/package/mssql-mcp)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
8
+
9
+ ## Standards Alignment
10
+
11
+ - Protocol target: [MCP draft/latest spec](https://modelcontextprotocol.io/specification/draft/server/tools)
12
+ - Transport spec: [Transports](https://modelcontextprotocol.io/specification/draft/basic/transports)
13
+ - Security: [Security best practices](https://modelcontextprotocol.io/docs/tutorials/security/security_best_practices)
14
+ - SDK: `@modelcontextprotocol/sdk` pinned to `1.28.0`
4
15
 
5
16
  ## 🚀 Quick Start
6
17
 
@@ -24,6 +35,7 @@ npm install -g mssql-mcp
24
35
  "DB_DATABASE": "your-database",
25
36
  "DB_USER": "your-username",
26
37
  "DB_PASSWORD": "your-password",
38
+ "DB_ENCRYPT": "true",
27
39
  "DB_TRUST_SERVER_CERTIFICATE": "true"
28
40
  }
29
41
  }
@@ -43,6 +55,7 @@ npm install -g mssql-mcp
43
55
  "DB_DATABASE": "your-database",
44
56
  "DB_USER": "your-username",
45
57
  "DB_PASSWORD": "your-password",
58
+ "DB_ENCRYPT": "true",
46
59
  "DB_TRUST_SERVER_CERTIFICATE": "true"
47
60
  }
48
61
  }
@@ -50,87 +63,179 @@ npm install -g mssql-mcp
50
63
  }
51
64
  ```
52
65
 
53
- > Replace with your actual database credentials. Server auto-connects using these.
66
+ **HTTP transport** (remote/hosted scenarios):
67
+ ```json
68
+ {
69
+ "servers": {
70
+ "mssql": {
71
+ "type": "http",
72
+ "url": "http://127.0.0.1:3001",
73
+ "env": {}
74
+ }
75
+ }
76
+ }
77
+ ```
78
+
79
+ > Replace with your actual database credentials. Credentials are read from the **server environment only** — never passed as tool parameters.
80
+
81
+ ## ��️ Tool Catalog
82
+
83
+ ### Primary tools (use these)
84
+
85
+ | Tool | Read-only | Description |
86
+ |------|-----------|-------------|
87
+ | `mssql_connect_database` | No | Connect using env variables. Idempotent. |
88
+ | `mssql_disconnect_database` | No | Close connection. Idempotent. |
89
+ | `mssql_connection_status` | ✅ | Connection state and pool metrics |
90
+ | `mssql_run_sql_query` | ⚠️ No | Execute arbitrary SQL. **May mutate data.** |
91
+ | `mssql_list_schema_objects` | ✅ | List tables/views/procedures/functions with pagination |
92
+ | `mssql_describe_table_columns` | ✅ | Column definitions for a table |
93
+ | `mssql_read_table_rows` | ✅ | Paginated rows with projection and safe WHERE |
94
+ | `mssql_execute_stored_procedure` | ⚠️ No | Execute a stored procedure |
95
+ | `mssql_list_databases` | ✅ | List all databases on the instance |
96
+
97
+ All data tools accept a `response_format` parameter (`"json"` | `"markdown"`, default `"json"`). Use `"markdown"` to get human-readable table output.
98
+
99
+ ### Deprecated aliases (still work for backward compatibility)
100
+
101
+ | Old name | Use instead |
102
+ |----------|-------------|
103
+ | `connect_database` | `mssql_connect_database` |
104
+ | `disconnect_database` | `mssql_disconnect_database` |
105
+ | `connection_status` | `mssql_connection_status` |
106
+ | `execute_query` | `mssql_run_sql_query` |
107
+ | `run_sql_query` | `mssql_run_sql_query` |
108
+ | `get_schema` | `mssql_list_schema_objects` |
109
+ | `list_schema_objects` | `mssql_list_schema_objects` |
110
+ | `describe_table` | `mssql_describe_table_columns` |
111
+ | `describe_table_columns` | `mssql_describe_table_columns` |
112
+ | `get_table_data` | `mssql_read_table_rows` |
113
+ | `read_table_rows` | `mssql_read_table_rows` |
114
+ | `execute_procedure` | `mssql_execute_stored_procedure` |
115
+ | `execute_stored_procedure` | `mssql_execute_stored_procedure` |
116
+ | `list_databases` | `mssql_list_databases` |
117
+
118
+ ## 🚌 Transport Modes
119
+
120
+ | Mode | Use when |
121
+ |------|----------|
122
+ | `stdio` (default) | Local IDE integration (Claude Desktop, Cursor, VS Code) |
123
+ | `http` | Remote/hosted deployment, testing with MCP Inspector |
54
124
 
55
- ## 🛠️ Available Tools
125
+ ```bash
126
+ # stdio (default)
127
+ node dist/src/index.js
56
128
 
57
- | Tool | Description |
58
- |------|-------------|
59
- | `execute_query` | Execute any SQL query with parameters |
60
- | `get_schema` | List database objects (tables, views, procedures) |
61
- | `describe_table` | Get detailed table structure |
62
- | `get_table_data` | Retrieve data with pagination |
63
- | `execute_procedure` | Execute stored procedures |
64
- | `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 |
129
+ # HTTP on 127.0.0.1:3001
130
+ MCP_TRANSPORT=http node dist/src/index.js
69
131
 
70
- All tools auto-connect using environment variables.
132
+ # Custom HTTP host/port
133
+ MCP_TRANSPORT=http MCP_HOST=0.0.0.0 MCP_PORT=8080 node dist/src/index.js
134
+ ```
71
135
 
72
136
  ## 🔧 Environment Variables
73
137
 
74
- | Variable | Required | Default |
75
- |----------|----------|---------|
76
- | `DB_SERVER` | ✅ | - |
77
- | `DB_DATABASE` | ✅ | - |
78
- | `DB_USER` | ✅ | - |
79
- | `DB_PASSWORD` | ✅ | - |
80
- | `DB_PORT` | ❌ | 1433 |
81
- | `DB_TRUST_SERVER_CERTIFICATE` | ❌ | true |
82
- | `DB_CONNECTION_TIMEOUT` | ❌ | 30000 |
83
- | `DB_REQUEST_TIMEOUT` | ❌ | 30000 |
138
+ ### Database connection
84
139
 
85
- ## 🛡️ Security
140
+ | Variable | Required | Default | Description |
141
+ |----------|----------|---------|-------------|
142
+ | `DB_SERVER` | ✅ | — | SQL Server hostname or IP |
143
+ | `DB_DATABASE` | ❌ | — | Database name |
144
+ | `DB_USER` | ❌ | — | Login username |
145
+ | `DB_PASSWORD` | ❌ | — | Login password |
146
+ | `DB_PORT` | ❌ | 1433 | TCP port |
147
+ | `DB_ENCRYPT` | ❌ | true | Enable TLS (required for Azure SQL) |
148
+ | `DB_TRUST_SERVER_CERTIFICATE` | ❌ | false | Trust self-signed certs |
149
+ | `DB_CONNECTION_TIMEOUT` | ❌ | 30000 | Connection timeout ms |
150
+ | `DB_REQUEST_TIMEOUT` | ❌ | 30000 | Query timeout ms |
86
151
 
87
- **✅ Supported:** All database operations (SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, DROP)
152
+ ### Transport
88
153
 
89
- **🚨 Blocked:** Server-level operations only (SHUTDOWN, XP_CMDSHELL, RECONFIGURE)
154
+ | Variable | Default | Description |
155
+ |----------|---------|-------------|
156
+ | `MCP_TRANSPORT` | `stdio` | `stdio` or `http` |
157
+ | `MCP_HOST` | `127.0.0.1` | HTTP bind address |
158
+ | `MCP_PORT` | `3001` | HTTP port |
90
159
 
91
- ## 🏆 Features
160
+ ## 🔒 Security Model
92
161
 
93
- - **Auto-Connection**: Environment-based, no manual steps
94
- - **Complete SQL Support**: All database operations
95
- - **No Rate Limiting**: Natural workflow
96
- - **Query Caching**: 5-minute TTL for SELECT queries
97
- - **Performance Monitoring**: Execution time tracking
98
- - ✅ **Latest MCP SDK**: v1.20.2 with 2025 protocol
99
- - ✅ **Clean Logging**: Minimal console output, MCP protocol compatible
162
+ - **No credential parameters**: All connection settings come from environment variables only. Tool inputs cannot override connection config.
163
+ - **Identifier validation**: Schema, table, and procedure names are validated against a safe identifier pattern before interpolation into SQL.
164
+ - **Parameterized queries**: All user-supplied values (WHERE clause values, column values) must be passed as named parameters via `@paramName` — never embedded in query strings.
165
+ - **Origin validation**: HTTP transport validates `Origin` header and only allows localhost by default.
166
+ - **SQL risk labeling**: `run_sql_query` and `execute_stored_procedure` are explicitly labeled as non-read-only and open-world.
100
167
 
101
- ## 🔍 Troubleshooting
168
+ ### ⚠️ SQL Risk Notes
102
169
 
103
- **❌ Auto-connection failed**
104
- - Set all required environment variables
105
- - Verify server accessibility and credentials
106
- - Check network connectivity
170
+ `run_sql_query` accepts arbitrary SQL including DDL and DML. To minimize risk:
171
+ - Use a least-privilege SQL login (SELECT-only where possible)
172
+ - Never run the server with a `sysadmin` or `sa` account
173
+ - Consider network firewall rules to limit what the server can reach
107
174
 
108
- **❌ SQL Security Alert**
109
- - Only server operations are blocked
110
- - Database operations should work normally
175
+ ## 📄 Pagination
111
176
 
112
- ## 📋 Version History
177
+ All list tools return a `pagination` object:
113
178
 
114
- ### v2.0.3 - Latest
115
- - ✅ Updated documentation with modern styling
116
- - ✅ Improved troubleshooting section
117
- - ✅ Enhanced feature descriptions
179
+ ```json
180
+ {
181
+ "count": 20,
182
+ "limit": 20,
183
+ "offset": 0,
184
+ "has_more": true,
185
+ "next_offset": 20,
186
+ "total_count": 150
187
+ }
188
+ ```
189
+
190
+ Default page size: **20 rows**. Maximum: **200 rows**.
191
+
192
+ Results are also truncated if the serialized payload exceeds 100KB, with a `truncation_message` explaining how many rows were dropped.
193
+
194
+ ## 🏗️ Architecture
118
195
 
119
- ### v2.0.2 - Performance & Compatibility
120
- - ✅ Simplified logging for MCP protocol compatibility
121
- - All console output moved to stderr
122
- - Clean, professional log messages
123
- - Version bump to 2.0.2
196
+ ```
197
+ src/
198
+ index.ts ← bootstrap (env, transport selection)
199
+ server.ts ← createServer() factory
200
+ constants.ts ← limits, defaults, protocol strings
201
+ config.ts ← env parsing
202
+ types.ts ← shared TypeScript interfaces
203
+ db/
204
+ connection.ts ← connection pool singleton
205
+ validators.ts ← SQL identifier validation
206
+ query-builders.ts ← safe parameterized query construction
207
+ tools/ ← one file per tool group
208
+ resources/ ← MCP resource handlers
209
+ transports/ ← stdio and HTTP transports
210
+ utils/
211
+ errors.ts ← error normalization helpers
212
+ format.ts ← JSON formatting, payload truncation
213
+ markdown.ts ← markdown table/list rendering helpers
214
+ pagination.ts ← pagination metadata helpers
215
+ ```
124
216
 
125
- ## 📄 License
217
+ ## 🧪 Inspector Smoke Test
126
218
 
127
- MIT License
219
+ ```bash
220
+ npx @modelcontextprotocol/inspector
221
+ ```
128
222
 
129
- ## 🆘 Support
223
+ Expected:
224
+ - stdio server connects
225
+ - Tool list renders with all tools
226
+ - `mssql_connection_status` returns JSON without a connection
227
+ - `mssql_connect_database` works when env variables are set
130
228
 
131
- - **Issues**: [GitHub Issues](https://github.com/BYMCS/mssql-mcp/issues)
132
- - **Repository**: [BYMCS/mssql-mcp](https://github.com/BYMCS/mssql-mcp)
229
+ ## 🔨 Development
230
+
231
+ ```bash
232
+ npm install
233
+ npm run typecheck # type check only
234
+ npm run build # compile TypeScript
235
+ npm test # run unit tests
236
+ npm run ci # typecheck + build + test
237
+ ```
133
238
 
134
- ---
239
+ ## License
135
240
 
136
- **🎉 v2.0.3: Enhanced documentation and user experience**
241
+ [MIT](LICENSE) © BYMCS
@@ -0,0 +1,39 @@
1
+ import { z } from "zod";
2
+ import type { DatabaseConfig } from "./types.js";
3
+ export declare const ConfigSchema: z.ZodObject<{
4
+ server: z.ZodString;
5
+ database: z.ZodOptional<z.ZodString>;
6
+ user: z.ZodOptional<z.ZodString>;
7
+ password: z.ZodOptional<z.ZodString>;
8
+ port: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
9
+ encrypt: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
10
+ trustServerCertificate: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
11
+ connectionTimeout: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
12
+ requestTimeout: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
13
+ }, "strip", z.ZodTypeAny, {
14
+ server: string;
15
+ port: number;
16
+ encrypt: boolean;
17
+ trustServerCertificate: boolean;
18
+ connectionTimeout: number;
19
+ requestTimeout: number;
20
+ database?: string | undefined;
21
+ user?: string | undefined;
22
+ password?: string | undefined;
23
+ }, {
24
+ server: string;
25
+ database?: string | undefined;
26
+ port?: number | undefined;
27
+ user?: string | undefined;
28
+ password?: string | undefined;
29
+ encrypt?: boolean | undefined;
30
+ trustServerCertificate?: boolean | undefined;
31
+ connectionTimeout?: number | undefined;
32
+ requestTimeout?: number | undefined;
33
+ }>;
34
+ export declare function loadConfigFromEnv(): DatabaseConfig;
35
+ export interface HttpConfig {
36
+ host: string;
37
+ port: number;
38
+ }
39
+ export declare function loadHttpConfig(): HttpConfig;
@@ -0,0 +1,37 @@
1
+ import { z } from "zod";
2
+ import { DEFAULT_PORT, DEFAULT_ENCRYPT, DEFAULT_TRUST_SERVER_CERTIFICATE, DEFAULT_CONNECTION_TIMEOUT, DEFAULT_REQUEST_TIMEOUT, } from "./constants.js";
3
+ export const ConfigSchema = z.object({
4
+ server: z.string().min(1, "Server address is required"),
5
+ database: z.string().optional(),
6
+ user: z.string().optional(),
7
+ password: z.string().optional(),
8
+ port: z.number().int().min(1).max(65535).optional().default(DEFAULT_PORT),
9
+ encrypt: z.boolean().optional().default(DEFAULT_ENCRYPT),
10
+ trustServerCertificate: z.boolean().optional().default(DEFAULT_TRUST_SERVER_CERTIFICATE),
11
+ connectionTimeout: z.number().int().min(1000).max(60000).optional().default(DEFAULT_CONNECTION_TIMEOUT),
12
+ requestTimeout: z.number().int().min(1000).max(300000).optional().default(DEFAULT_REQUEST_TIMEOUT),
13
+ });
14
+ export function loadConfigFromEnv() {
15
+ const raw = {
16
+ server: process.env.DB_SERVER,
17
+ database: process.env.DB_DATABASE,
18
+ user: process.env.DB_USER,
19
+ password: process.env.DB_PASSWORD,
20
+ port: process.env.DB_PORT ? parseInt(process.env.DB_PORT, 10) : DEFAULT_PORT,
21
+ encrypt: process.env.DB_ENCRYPT !== "false",
22
+ trustServerCertificate: process.env.DB_TRUST_SERVER_CERTIFICATE === "true",
23
+ connectionTimeout: process.env.DB_CONNECTION_TIMEOUT
24
+ ? parseInt(process.env.DB_CONNECTION_TIMEOUT, 10)
25
+ : DEFAULT_CONNECTION_TIMEOUT,
26
+ requestTimeout: process.env.DB_REQUEST_TIMEOUT
27
+ ? parseInt(process.env.DB_REQUEST_TIMEOUT, 10)
28
+ : DEFAULT_REQUEST_TIMEOUT,
29
+ };
30
+ return ConfigSchema.parse(raw);
31
+ }
32
+ export function loadHttpConfig() {
33
+ return {
34
+ host: process.env.MCP_HOST ?? "127.0.0.1",
35
+ port: process.env.MCP_PORT ? parseInt(process.env.MCP_PORT, 10) : 3001,
36
+ };
37
+ }
@@ -0,0 +1,15 @@
1
+ export declare const SERVER_NAME = "mssql-mcp-server";
2
+ export declare const SERVER_VERSION = "2.3.0";
3
+ export declare const DEFAULT_PORT = 1433;
4
+ export declare const DEFAULT_ENCRYPT = true;
5
+ export declare const DEFAULT_TRUST_SERVER_CERTIFICATE = false;
6
+ export declare const DEFAULT_CONNECTION_TIMEOUT = 30000;
7
+ export declare const DEFAULT_REQUEST_TIMEOUT = 30000;
8
+ export declare const POOL_MAX = 10;
9
+ export declare const POOL_MIN = 0;
10
+ export declare const POOL_IDLE_TIMEOUT_MS = 30000;
11
+ export declare const DEFAULT_PAGE_SIZE = 20;
12
+ export declare const MAX_PAGE_SIZE = 200;
13
+ export declare const MAX_TEXT_PAYLOAD_BYTES = 100000;
14
+ export declare const HTTP_DEFAULT_HOST = "127.0.0.1";
15
+ export declare const HTTP_DEFAULT_PORT = 3001;
@@ -0,0 +1,15 @@
1
+ export const SERVER_NAME = "mssql-mcp-server";
2
+ export const SERVER_VERSION = "2.3.0";
3
+ export const DEFAULT_PORT = 1433;
4
+ export const DEFAULT_ENCRYPT = true;
5
+ export const DEFAULT_TRUST_SERVER_CERTIFICATE = false;
6
+ export const DEFAULT_CONNECTION_TIMEOUT = 30000;
7
+ export const DEFAULT_REQUEST_TIMEOUT = 30000;
8
+ export const POOL_MAX = 10;
9
+ export const POOL_MIN = 0;
10
+ export const POOL_IDLE_TIMEOUT_MS = 30000;
11
+ export const DEFAULT_PAGE_SIZE = 20;
12
+ export const MAX_PAGE_SIZE = 200;
13
+ export const MAX_TEXT_PAYLOAD_BYTES = 100_000;
14
+ export const HTTP_DEFAULT_HOST = "127.0.0.1";
15
+ export const HTTP_DEFAULT_PORT = 3001;
@@ -0,0 +1,8 @@
1
+ import sql from "mssql";
2
+ import type { DatabaseConfig, ConnectionState } from "../types.js";
3
+ export declare function connectPool(config: DatabaseConfig): Promise<void>;
4
+ export declare function disconnectPool(): Promise<void>;
5
+ export declare function getPool(): sql.ConnectionPool | null;
6
+ export declare function requirePool(): sql.ConnectionPool;
7
+ export declare function getConnectionState(): ConnectionState;
8
+ export declare function closePoolOnShutdown(): Promise<void>;
@@ -0,0 +1,80 @@
1
+ import sql from "mssql";
2
+ import { POOL_MAX, POOL_MIN, POOL_IDLE_TIMEOUT_MS } from "../constants.js";
3
+ let pool = null;
4
+ let currentConfig = null;
5
+ export async function connectPool(config) {
6
+ if (pool) {
7
+ console.error("Closing existing connection...");
8
+ await pool.close();
9
+ pool = null;
10
+ }
11
+ console.error(`Connecting to ${config.server}:${config.port}`);
12
+ const newPool = new sql.ConnectionPool({
13
+ server: config.server,
14
+ database: config.database,
15
+ user: config.user,
16
+ password: config.password,
17
+ port: config.port,
18
+ options: {
19
+ encrypt: config.encrypt,
20
+ trustServerCertificate: config.trustServerCertificate,
21
+ enableArithAbort: true,
22
+ },
23
+ connectionTimeout: config.connectionTimeout,
24
+ requestTimeout: config.requestTimeout,
25
+ pool: { max: POOL_MAX, min: POOL_MIN, idleTimeoutMillis: POOL_IDLE_TIMEOUT_MS },
26
+ });
27
+ newPool.on("error", (err) => {
28
+ console.error("Pool error:", err);
29
+ });
30
+ try {
31
+ await newPool.connect();
32
+ pool = newPool;
33
+ currentConfig = config;
34
+ console.error(`Connected to ${config.server}${config.database ? `/${config.database}` : ""}`);
35
+ }
36
+ catch (err) {
37
+ try {
38
+ await newPool.close();
39
+ }
40
+ catch { /* ignore */ }
41
+ throw err;
42
+ }
43
+ }
44
+ export async function disconnectPool() {
45
+ if (pool) {
46
+ await pool.close();
47
+ pool = null;
48
+ currentConfig = null;
49
+ }
50
+ }
51
+ export function getPool() {
52
+ return pool;
53
+ }
54
+ export function requirePool() {
55
+ if (!pool?.connected) {
56
+ throw new Error("No active database connection. Use connect_database first.");
57
+ }
58
+ return pool;
59
+ }
60
+ export function getConnectionState() {
61
+ return {
62
+ connected: pool?.connected ?? false,
63
+ config: currentConfig
64
+ ? { server: currentConfig.server, database: currentConfig.database, port: currentConfig.port }
65
+ : null,
66
+ pool_info: pool
67
+ ? { size: pool.size, available: pool.available, pending: pool.pending, borrowed: pool.borrowed }
68
+ : null,
69
+ };
70
+ }
71
+ export async function closePoolOnShutdown() {
72
+ if (pool) {
73
+ try {
74
+ await pool.close();
75
+ }
76
+ catch { /* ignore */ }
77
+ pool = null;
78
+ currentConfig = null;
79
+ }
80
+ }
@@ -0,0 +1,3 @@
1
+ export declare function buildSelectQuery(schemaName: string, tableName: string, columns: string[] | null, orderBy: string | null, offset: number, limit: number): string;
2
+ export declare function buildSelectWithWhereQuery(schemaName: string, tableName: string, columns: string[] | null, whereClause: string, orderBy: string | null, offset: number, limit: number): string;
3
+ export declare function buildSchemaObjectsQuery(objectType: "tables" | "views" | "procedures" | "functions" | "all", schemaName?: string): string;
@@ -0,0 +1,58 @@
1
+ import { bracketIdentifier } from "./validators.js";
2
+ export function buildSelectQuery(schemaName, tableName, columns, orderBy, offset, limit) {
3
+ const safeSchema = bracketIdentifier(schemaName);
4
+ const safeTable = bracketIdentifier(tableName);
5
+ const projection = columns && columns.length > 0 ? columns.map(bracketIdentifier).join(", ") : "*";
6
+ const order = orderBy ?? "(SELECT NULL)";
7
+ return (`SELECT ${projection} FROM ${safeSchema}.${safeTable}` +
8
+ ` ORDER BY ${order}` +
9
+ ` OFFSET ${offset} ROWS FETCH NEXT ${limit} ROWS ONLY`);
10
+ }
11
+ export function buildSelectWithWhereQuery(schemaName, tableName, columns, whereClause, orderBy, offset, limit) {
12
+ const safeSchema = bracketIdentifier(schemaName);
13
+ const safeTable = bracketIdentifier(tableName);
14
+ const projection = columns && columns.length > 0 ? columns.map(bracketIdentifier).join(", ") : "*";
15
+ const order = orderBy ?? "(SELECT NULL)";
16
+ return (`SELECT ${projection} FROM ${safeSchema}.${safeTable}` +
17
+ ` WHERE ${whereClause}` +
18
+ ` ORDER BY ${order}` +
19
+ ` OFFSET ${offset} ROWS FETCH NEXT ${limit} ROWS ONLY`);
20
+ }
21
+ export function buildSchemaObjectsQuery(objectType, schemaName) {
22
+ const parts = [];
23
+ const schemaFilter = schemaName ? "TABLE_SCHEMA = @schemaName" : null;
24
+ const routineSchemaFilter = schemaName ? "ROUTINE_SCHEMA = @schemaName" : null;
25
+ if (objectType === "tables" || objectType === "all") {
26
+ parts.push(`
27
+ SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE, 'table' as OBJECT_TYPE
28
+ FROM INFORMATION_SCHEMA.TABLES
29
+ ${schemaFilter ? `WHERE ${schemaFilter}` : ""}
30
+ `);
31
+ }
32
+ if (objectType === "views" || objectType === "all") {
33
+ parts.push(`
34
+ SELECT TABLE_SCHEMA, TABLE_NAME, 'VIEW' as TABLE_TYPE, 'view' as OBJECT_TYPE
35
+ FROM INFORMATION_SCHEMA.VIEWS
36
+ ${schemaFilter ? `WHERE ${schemaFilter}` : ""}
37
+ `);
38
+ }
39
+ if (objectType === "procedures" || objectType === "all") {
40
+ parts.push(`
41
+ SELECT ROUTINE_SCHEMA as TABLE_SCHEMA, ROUTINE_NAME as TABLE_NAME,
42
+ 'PROCEDURE' as TABLE_TYPE, 'procedure' as OBJECT_TYPE
43
+ FROM INFORMATION_SCHEMA.ROUTINES
44
+ WHERE ROUTINE_TYPE = 'PROCEDURE'
45
+ ${routineSchemaFilter ? `AND ${routineSchemaFilter}` : ""}
46
+ `);
47
+ }
48
+ if (objectType === "functions" || objectType === "all") {
49
+ parts.push(`
50
+ SELECT ROUTINE_SCHEMA as TABLE_SCHEMA, ROUTINE_NAME as TABLE_NAME,
51
+ 'FUNCTION' as TABLE_TYPE, 'function' as OBJECT_TYPE
52
+ FROM INFORMATION_SCHEMA.ROUTINES
53
+ WHERE ROUTINE_TYPE = 'FUNCTION'
54
+ ${routineSchemaFilter ? `AND ${routineSchemaFilter}` : ""}
55
+ `);
56
+ }
57
+ return parts.join(" UNION ALL ") + " ORDER BY TABLE_SCHEMA, TABLE_NAME";
58
+ }
@@ -0,0 +1,5 @@
1
+ export declare const SAFE_IDENTIFIER_RE: RegExp;
2
+ export declare function isValidIdentifier(name: string): boolean;
3
+ export declare function validateIdentifier(name: string, label: string): string;
4
+ export declare function bracketIdentifier(name: string): string;
5
+ export declare function validateOrderBy(orderBy: string): string;
@@ -0,0 +1,25 @@
1
+ // Safe SQL identifier validation.
2
+ // Only validates identifier names (schema, table, procedure) — never user values.
3
+ // User values must always be passed as mssql parameters, never interpolated.
4
+ export const SAFE_IDENTIFIER_RE = /^[a-zA-Z_][a-zA-Z0-9_$#@]{0,127}$/;
5
+ export function isValidIdentifier(name) {
6
+ return SAFE_IDENTIFIER_RE.test(name);
7
+ }
8
+ export function validateIdentifier(name, label) {
9
+ if (!isValidIdentifier(name)) {
10
+ throw new Error(`Invalid ${label} "${name}". Identifiers must start with a letter or underscore and contain only letters, digits, underscores, $, #, @.`);
11
+ }
12
+ return name;
13
+ }
14
+ export function bracketIdentifier(name) {
15
+ validateIdentifier(name, "identifier");
16
+ return `[${name}]`;
17
+ }
18
+ // ORDER BY: allows column names (optionally bracketed), dotted refs, ASC/DESC
19
+ const ORDER_BY_RE = /^(\[?[a-zA-Z_][a-zA-Z0-9_$#@.]*\]?(\s+(ASC|DESC))?)((\s*,\s*)\[?[a-zA-Z_][a-zA-Z0-9_$#@.]*\]?(\s+(ASC|DESC))?)*$/i;
20
+ export function validateOrderBy(orderBy) {
21
+ if (!ORDER_BY_RE.test(orderBy.trim())) {
22
+ throw new Error("Invalid ORDER BY clause. Only column names, commas, and ASC/DESC direction keywords are allowed.");
23
+ }
24
+ return orderBy.trim();
25
+ }
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+ import dotenv from "dotenv";
3
+ import { createServer } from "./server.js";
4
+ import { runStdioTransport } from "./transports/stdio.js";
5
+ import { runHttpTransport } from "./transports/http.js";
6
+ import { loadHttpConfig } from "./config.js";
7
+ import { SERVER_VERSION } from "./constants.js";
8
+ dotenv.config();
9
+ async function main() {
10
+ const transport = process.env.MCP_TRANSPORT ?? "stdio";
11
+ const server = createServer();
12
+ if (transport === "http") {
13
+ const httpConfig = loadHttpConfig();
14
+ console.error(`MSSQL MCP Server v${SERVER_VERSION} starting (HTTP mode)...`);
15
+ await runHttpTransport(server, httpConfig);
16
+ }
17
+ else {
18
+ console.error(`MSSQL MCP Server v${SERVER_VERSION} starting (stdio mode)...`);
19
+ await runStdioTransport(server);
20
+ console.error("Server ready.");
21
+ }
22
+ }
23
+ main().catch((err) => {
24
+ console.error("Fatal startup error:", err);
25
+ process.exit(1);
26
+ });
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerConnectionResource(server: McpServer): void;
@@ -0,0 +1,19 @@
1
+ import { getConnectionState } from "../db/connection.js";
2
+ export function registerConnectionResource(server) {
3
+ server.registerResource("connection-info", "mssql://connection/info", {
4
+ title: "Connection Info",
5
+ description: "Current MSSQL connection status and configuration (no credentials exposed).",
6
+ mimeType: "application/json",
7
+ }, async () => {
8
+ const state = getConnectionState();
9
+ return {
10
+ contents: [
11
+ {
12
+ uri: "mssql://connection/info",
13
+ text: JSON.stringify(state, null, 2),
14
+ mimeType: "application/json",
15
+ },
16
+ ],
17
+ };
18
+ });
19
+ }
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerMetadataResources(server: McpServer): void;