mssql-mcp 2.1.1 → 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 +161 -89
  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 +10 -6
  59. package/dist/index.js +0 -648
  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,7 +1,18 @@
1
- # MS SQL Server MCP Server v2.1.1
1
+ # MS SQL Server MCP Server v2.3.0
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
+ [![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`
15
+
5
16
  ## 🚀 Quick Start
6
17
 
7
18
  ### 1. Install
@@ -52,118 +63,179 @@ npm install -g mssql-mcp
52
63
  }
53
64
  ```
54
65
 
55
- > Replace with your actual database credentials.
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 |
56
124
 
57
- ## 🛠️ Available Tools
125
+ ```bash
126
+ # stdio (default)
127
+ node dist/src/index.js
58
128
 
59
- | Tool | Description |
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 |
64
- | `execute_query` | Execute any SQL query with parameters |
65
- | `get_schema` | List database objects (tables, views, procedures) |
66
- | `describe_table` | Get detailed table structure |
67
- | `get_table_data` | Retrieve data with pagination |
68
- | `execute_procedure` | Execute stored procedures |
69
- | `list_databases` | List all databases |
129
+ # HTTP on 127.0.0.1:3001
130
+ MCP_TRANSPORT=http node dist/src/index.js
131
+
132
+ # Custom HTTP host/port
133
+ MCP_TRANSPORT=http MCP_HOST=0.0.0.0 MCP_PORT=8080 node dist/src/index.js
134
+ ```
70
135
 
71
136
  ## 🔧 Environment Variables
72
137
 
138
+ ### Database connection
139
+
73
140
  | Variable | Required | Default | Description |
74
141
  |----------|----------|---------|-------------|
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
- ```
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 |
93
151
 
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
- ```
152
+ ### Transport
102
153
 
103
- ## 🏆 Features
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 |
104
159
 
105
- - **MCP SDK 1.25.1**: Latest Model Context Protocol SDK
106
- - ✅ **Azure SQL Compatible**: TLS encryption enabled by default
107
- - ✅ **Complete SQL Support**: All database operations
108
- - ✅ **Parameterized Queries**: SQL injection protection
109
- - ✅ **Connection Pooling**: Efficient resource management
110
- - ✅ **Performance Monitoring**: Execution time tracking
160
+ ## 🔒 Security Model
111
161
 
112
- ## 📋 Usage Examples
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.
113
167
 
114
- ### Connect to Database
115
- ```
116
- Use the connect_database tool to establish a connection.
117
- ```
168
+ ### ⚠️ SQL Risk Notes
118
169
 
119
- ### Execute a Query
120
- ```sql
121
- SELECT TOP 10 * FROM Customers WHERE Country = @country
122
- -- With parameters: { "country": "USA" }
123
- ```
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
124
174
 
125
- ### Get Table Schema
126
- ```
127
- Use describe_table with tableName: "Customers" to see column details.
128
- ```
175
+ ## 📄 Pagination
129
176
 
130
- ## 🔍 Troubleshooting
177
+ All list tools return a `pagination` object:
131
178
 
132
- **❌ Connection failed**
133
- - Verify all required environment variables are set
134
- - Check server accessibility and credentials
135
- - Ensure network connectivity to SQL Server
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
+ ```
136
189
 
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`
190
+ Default page size: **20 rows**. Maximum: **200 rows**.
141
191
 
142
- ## 📋 Version History
192
+ Results are also truncated if the serialized payload exceeds 100KB, with a `truncation_message` explaining how many rows were dropped.
143
193
 
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`
194
+ ## 🏗️ Architecture
149
195
 
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
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
+ ```
154
216
 
155
- ### v2.0.3
156
- - ✅ Documentation improvements
217
+ ## 🧪 Inspector Smoke Test
157
218
 
158
- ## 📄 License
219
+ ```bash
220
+ npx @modelcontextprotocol/inspector
221
+ ```
159
222
 
160
- MIT License
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
161
228
 
162
- ## 🆘 Support
229
+ ## 🔨 Development
163
230
 
164
- - **Issues**: [GitHub Issues](https://github.com/BYMCS/mssql-mcp/issues)
165
- - **Repository**: [BYMCS/mssql-mcp](https://github.com/BYMCS/mssql-mcp)
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
+ ```
166
238
 
167
- ---
239
+ ## License
168
240
 
169
- **🎉 v2.1.1: Azure SQL compatibility with DB_ENCRYPT support**
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;