mcp-server-db2i 1.0.0 → 1.2.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 +142 -7
- package/dist/config.d.ts +95 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +136 -5
- package/dist/config.js.map +1 -1
- package/dist/db/connection.d.ts +0 -13
- package/dist/db/connection.d.ts.map +1 -1
- package/dist/db/connection.js +15 -32
- package/dist/db/connection.js.map +1 -1
- package/dist/db/queries.d.ts +22 -0
- package/dist/db/queries.d.ts.map +1 -1
- package/dist/db/queries.js +42 -35
- package/dist/db/queries.js.map +1 -1
- package/dist/index.js +43 -136
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +39 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +133 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/metadata.d.ts +51 -194
- package/dist/tools/metadata.d.ts.map +1 -1
- package/dist/tools/metadata.js +35 -167
- package/dist/tools/metadata.js.map +1 -1
- package/dist/tools/query.d.ts +8 -21
- package/dist/tools/query.d.ts.map +1 -1
- package/dist/tools/query.js +18 -24
- package/dist/tools/query.js.map +1 -1
- package/dist/utils/logger.d.ts +48 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +124 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/rateLimiter.d.ts +90 -0
- package/dist/utils/rateLimiter.d.ts.map +1 -0
- package/dist/utils/rateLimiter.js +215 -0
- package/dist/utils/rateLimiter.js.map +1 -0
- package/dist/utils/security/sqlSecurityValidator.d.ts +101 -0
- package/dist/utils/security/sqlSecurityValidator.d.ts.map +1 -0
- package/dist/utils/security/sqlSecurityValidator.js +312 -0
- package/dist/utils/security/sqlSecurityValidator.js.map +1 -0
- package/package.json +17 -3
package/README.md
CHANGED
|
@@ -20,9 +20,11 @@ A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server for IB
|
|
|
20
20
|
| `list_tables` | List tables in a schema (with optional filter) |
|
|
21
21
|
| `describe_table` | Get detailed column information |
|
|
22
22
|
| `list_views` | List views in a schema (with optional filter) |
|
|
23
|
-
| `list_indexes` | List indexes for a table |
|
|
23
|
+
| `list_indexes` | List SQL indexes for a table |
|
|
24
24
|
| `get_table_constraints` | Get primary keys, foreign keys, unique constraints |
|
|
25
25
|
|
|
26
|
+
> **Note:** `list_indexes` and `get_table_constraints` query the `QSYS2` SQL catalog views and only return SQL-defined objects. Legacy DDS Logical Files and Physical File constraints are not included. This is standard DB2 for i behavior.
|
|
27
|
+
|
|
26
28
|
### Filter Syntax
|
|
27
29
|
|
|
28
30
|
The list tools support pattern matching:
|
|
@@ -70,11 +72,26 @@ DB2I_HOSTNAME=your-ibm-i-host.com
|
|
|
70
72
|
DB2I_USERNAME=your-username
|
|
71
73
|
DB2I_PASSWORD=your-password
|
|
72
74
|
|
|
73
|
-
# Optional
|
|
75
|
+
# Optional - Database
|
|
74
76
|
DB2I_PORT=446 # Default: 446
|
|
75
77
|
DB2I_DATABASE=*LOCAL # Default: *LOCAL
|
|
76
78
|
DB2I_SCHEMA=your-default-schema # Default schema for all tools (can be overridden per-call)
|
|
77
79
|
DB2I_JDBC_OPTIONS=naming=system;date format=iso
|
|
80
|
+
|
|
81
|
+
# Optional - Logging
|
|
82
|
+
LOG_LEVEL=info # debug, info, warn, error, fatal (default: info)
|
|
83
|
+
NODE_ENV=production # production = JSON logs, development = pretty logs
|
|
84
|
+
LOG_PRETTY=true # Override: force pretty (true) or JSON (false) logs
|
|
85
|
+
LOG_COLORS=true # Override: force colors on/off (auto-detected by default)
|
|
86
|
+
|
|
87
|
+
# Optional - Rate Limiting
|
|
88
|
+
RATE_LIMIT_WINDOW_MS=900000 # Time window in ms (default: 900000 = 15 minutes)
|
|
89
|
+
RATE_LIMIT_MAX_REQUESTS=100 # Max requests per window (default: 100)
|
|
90
|
+
RATE_LIMIT_ENABLED=true # Set to 'false' to disable (default: true)
|
|
91
|
+
|
|
92
|
+
# Optional - Query Limits
|
|
93
|
+
QUERY_DEFAULT_LIMIT=1000 # Default rows returned (default: 1000)
|
|
94
|
+
QUERY_MAX_LIMIT=10000 # Maximum rows allowed (default: 10000)
|
|
78
95
|
```
|
|
79
96
|
|
|
80
97
|
### Environment Variables
|
|
@@ -82,12 +99,25 @@ DB2I_JDBC_OPTIONS=naming=system;date format=iso
|
|
|
82
99
|
| Variable | Required | Default | Description |
|
|
83
100
|
|----------|----------|---------|-------------|
|
|
84
101
|
| `DB2I_HOSTNAME` | Yes | - | IBM i hostname or IP address |
|
|
85
|
-
| `DB2I_USERNAME` | Yes | - | IBM i user profile |
|
|
86
|
-
| `DB2I_PASSWORD` | Yes | - | User password |
|
|
102
|
+
| `DB2I_USERNAME` | Yes* | - | IBM i user profile |
|
|
103
|
+
| `DB2I_PASSWORD` | Yes* | - | User password |
|
|
104
|
+
| `DB2I_USERNAME_FILE` | No | - | Path to file containing username (overrides `DB2I_USERNAME`) |
|
|
105
|
+
| `DB2I_PASSWORD_FILE` | No | - | Path to file containing password (overrides `DB2I_PASSWORD`) |
|
|
87
106
|
| `DB2I_PORT` | No | `446` | JDBC port (446 is standard for IBM i) |
|
|
88
107
|
| `DB2I_DATABASE` | No | `*LOCAL` | Database name |
|
|
89
108
|
| `DB2I_SCHEMA` | No | - | Default schema/library for tools. If set, you don't need to specify schema in each tool call. |
|
|
90
109
|
| `DB2I_JDBC_OPTIONS` | No | - | Additional JDBC options (semicolon-separated) |
|
|
110
|
+
| `LOG_LEVEL` | No | `info` | Log level: `debug`, `info`, `warn`, `error`, `fatal` |
|
|
111
|
+
| `NODE_ENV` | No | - | Set to `production` for JSON logs, otherwise pretty-printed |
|
|
112
|
+
| `LOG_PRETTY` | No | - | Override log format: `true` = pretty, `false` = JSON |
|
|
113
|
+
| `LOG_COLORS` | No | auto | Override colors: `true`/`false` (auto-detects TTY by default) |
|
|
114
|
+
| `RATE_LIMIT_WINDOW_MS` | No | `900000` | Rate limit time window in milliseconds (15 min) |
|
|
115
|
+
| `RATE_LIMIT_MAX_REQUESTS` | No | `100` | Maximum requests allowed per window |
|
|
116
|
+
| `RATE_LIMIT_ENABLED` | No | `true` | Set to `false` or `0` to disable rate limiting |
|
|
117
|
+
| `QUERY_DEFAULT_LIMIT` | No | `1000` | Default number of rows returned by queries |
|
|
118
|
+
| `QUERY_MAX_LIMIT` | No | `10000` | Maximum rows allowed (caps user-provided limits) |
|
|
119
|
+
|
|
120
|
+
*Either the environment variable or the corresponding `*_FILE` variable must be set. File-based secrets take priority when both are provided.
|
|
91
121
|
|
|
92
122
|
### JDBC Options
|
|
93
123
|
|
|
@@ -223,20 +253,124 @@ npm run build
|
|
|
223
253
|
|
|
224
254
|
# Run production build
|
|
225
255
|
npm start
|
|
256
|
+
|
|
257
|
+
# Run tests
|
|
258
|
+
npm test
|
|
259
|
+
|
|
260
|
+
# Run tests in watch mode
|
|
261
|
+
npm run test:watch
|
|
262
|
+
|
|
263
|
+
# Lint code
|
|
264
|
+
npm run lint
|
|
265
|
+
|
|
266
|
+
# Lint and fix
|
|
267
|
+
npm run lint:fix
|
|
268
|
+
|
|
269
|
+
# Type check
|
|
270
|
+
npm run typecheck
|
|
226
271
|
```
|
|
227
272
|
|
|
228
273
|
## Security
|
|
229
274
|
|
|
230
275
|
- **Read-only access**: Only SELECT statements are permitted
|
|
231
|
-
- **No credentials in code**: All sensitive data via environment variables
|
|
232
|
-
- **Query validation**:
|
|
233
|
-
- **Result limiting**: Default limit of 1000 rows
|
|
276
|
+
- **No credentials in code**: All sensitive data via environment variables or file-based secrets
|
|
277
|
+
- **Query validation**: AST-based SQL parsing plus regex validation blocks dangerous operations
|
|
278
|
+
- **Result limiting**: Default limit of 1000 rows, configurable max limit (default: 10000)
|
|
279
|
+
- **Rate limiting**: Configurable request throttling to prevent abuse (100 req/15 min default)
|
|
280
|
+
- **Structured logging**: Automatic redaction of sensitive fields like passwords
|
|
281
|
+
|
|
282
|
+
### Rate Limiting
|
|
283
|
+
|
|
284
|
+
The server includes built-in rate limiting to protect the IBM i database from excessive queries:
|
|
285
|
+
|
|
286
|
+
- **Default**: 100 requests per 15-minute window
|
|
287
|
+
- **Scope**: Per server instance (for stdio transport, this effectively means per-client since each MCP client spawns its own server process)
|
|
288
|
+
- **Configurable**: Adjust via `RATE_LIMIT_*` environment variables or disable entirely
|
|
289
|
+
|
|
290
|
+
When the rate limit is exceeded, queries return an error with `waitTimeSeconds` indicating when to retry.
|
|
291
|
+
|
|
292
|
+
### Credential Management
|
|
293
|
+
|
|
294
|
+
The server supports multiple methods for providing credentials, listed from most to least secure:
|
|
295
|
+
|
|
296
|
+
#### Option 1: Docker Secrets (Recommended for Production)
|
|
297
|
+
|
|
298
|
+
Docker secrets provide the most secure credential management. Secrets are mounted as files and never exposed in environment variables or process listings.
|
|
299
|
+
|
|
300
|
+
1. Create secret files:
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
mkdir -p ./secrets
|
|
304
|
+
echo "your-username" > ./secrets/db2i_username.txt
|
|
305
|
+
echo "your-password" > ./secrets/db2i_password.txt
|
|
306
|
+
chmod 600 ./secrets/*.txt
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
2. Configure docker-compose.yml to use secrets:
|
|
310
|
+
|
|
311
|
+
```yaml
|
|
312
|
+
services:
|
|
313
|
+
mcp-server-db2i:
|
|
314
|
+
# ... other config ...
|
|
315
|
+
environment:
|
|
316
|
+
- DB2I_HOSTNAME=${DB2I_HOSTNAME}
|
|
317
|
+
- DB2I_USERNAME_FILE=/run/secrets/db2i_username
|
|
318
|
+
- DB2I_PASSWORD_FILE=/run/secrets/db2i_password
|
|
319
|
+
secrets:
|
|
320
|
+
- db2i_username
|
|
321
|
+
- db2i_password
|
|
322
|
+
|
|
323
|
+
secrets:
|
|
324
|
+
db2i_username:
|
|
325
|
+
file: ./secrets/db2i_username.txt
|
|
326
|
+
db2i_password:
|
|
327
|
+
file: ./secrets/db2i_password.txt
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
For Docker Swarm or Kubernetes, use their native secret management instead of file-based secrets.
|
|
331
|
+
|
|
332
|
+
#### Option 2: External Secret Management
|
|
333
|
+
|
|
334
|
+
For enterprise deployments, integrate with secret management systems:
|
|
335
|
+
|
|
336
|
+
- **HashiCorp Vault**: Inject secrets at runtime
|
|
337
|
+
- **AWS Secrets Manager**: Use IAM roles for access
|
|
338
|
+
- **Azure Key Vault**: Integrate with managed identities
|
|
339
|
+
|
|
340
|
+
These systems can populate the `*_FILE` environment variables or inject secrets directly.
|
|
341
|
+
|
|
342
|
+
#### Option 3: Environment Variables (Development Only)
|
|
343
|
+
|
|
344
|
+
Plain environment variables are convenient for development but expose credentials through:
|
|
345
|
+
- `docker inspect` output
|
|
346
|
+
- Process listings (`ps aux`)
|
|
347
|
+
- Shell history
|
|
348
|
+
- Log files
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
# .env file (ensure it's in .gitignore)
|
|
352
|
+
DB2I_USERNAME=your-username
|
|
353
|
+
DB2I_PASSWORD=your-password
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
**Warning**: Never commit `.env` files or credentials to version control.
|
|
357
|
+
|
|
358
|
+
### File-Based Secret Variables
|
|
359
|
+
|
|
360
|
+
| Variable | Description |
|
|
361
|
+
|----------|-------------|
|
|
362
|
+
| `DB2I_USERNAME_FILE` | Path to file containing username (takes priority over `DB2I_USERNAME`) |
|
|
363
|
+
| `DB2I_PASSWORD_FILE` | Path to file containing password (takes priority over `DB2I_PASSWORD`) |
|
|
234
364
|
|
|
235
365
|
## Compatibility
|
|
236
366
|
|
|
237
367
|
- IBM i V7R3 and later (V7R5 recommended)
|
|
238
368
|
- Works with any IBM i system accessible via JDBC over TCP/IP
|
|
239
369
|
|
|
370
|
+
## Related Projects
|
|
371
|
+
|
|
372
|
+
- **[IBM ibmi-mcp-server](https://github.com/IBM/ibmi-mcp-server)** - IBM's official MCP server for IBM i systems. Offers YAML-based SQL tool definitions, AI agent frameworks, and production deployment options. Requires [Mapepire](https://mapepire-ibmi.github.io/) to be installed on your IBM i system, but if you can manage that prerequisite, it's worth checking out for more advanced use cases.
|
|
373
|
+
|
|
240
374
|
## Contributing
|
|
241
375
|
|
|
242
376
|
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
@@ -250,3 +384,4 @@ MIT License - see [LICENSE](LICENSE) for details.
|
|
|
250
384
|
- [node-jt400](https://www.npmjs.com/package/node-jt400) - JT400 JDBC driver wrapper for Node.js
|
|
251
385
|
- [Model Context Protocol](https://modelcontextprotocol.io/) - The protocol specification
|
|
252
386
|
- [@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/typescript-sdk) - Official TypeScript SDK
|
|
387
|
+
- [IBM ibmi-mcp-server](https://github.com/IBM/ibmi-mcp-server) - SQL security validation patterns inspired by their approach to AST-based query validation
|
package/dist/config.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Configuration module for IBM DB2i MCP Server
|
|
3
3
|
* Handles environment variables and JDBC connection options
|
|
4
|
+
*
|
|
5
|
+
* Supports file-based secrets (e.g., Docker secrets) via *_FILE environment variables.
|
|
6
|
+
* File-based secrets take priority over plain environment variables.
|
|
4
7
|
*/
|
|
5
8
|
export interface DB2iConfig {
|
|
6
9
|
hostname: string;
|
|
@@ -12,7 +15,48 @@ export interface DB2iConfig {
|
|
|
12
15
|
jdbcOptions: Record<string, string>;
|
|
13
16
|
}
|
|
14
17
|
/**
|
|
15
|
-
*
|
|
18
|
+
* Valid log levels for the application
|
|
19
|
+
*/
|
|
20
|
+
export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'fatal';
|
|
21
|
+
/**
|
|
22
|
+
* Get the configured log level from environment
|
|
23
|
+
* Defaults to 'info' if not set or invalid
|
|
24
|
+
*/
|
|
25
|
+
export declare function getLogLevel(): LogLevel;
|
|
26
|
+
/**
|
|
27
|
+
* Read a secret value from a file.
|
|
28
|
+
* Docker secrets are typically mounted at /run/secrets/<secret_name>
|
|
29
|
+
*
|
|
30
|
+
* @param filePath - Path to the file containing the secret
|
|
31
|
+
* @returns The secret value with leading/trailing whitespace trimmed
|
|
32
|
+
* @throws Error if file cannot be read
|
|
33
|
+
*/
|
|
34
|
+
export declare function readSecretFromFile(filePath: string): string;
|
|
35
|
+
/**
|
|
36
|
+
* Get a secret value from either a file or environment variable.
|
|
37
|
+
* File-based secrets take priority (more secure).
|
|
38
|
+
*
|
|
39
|
+
* @param envVar - Name of the environment variable containing the value
|
|
40
|
+
* @param fileEnvVar - Name of the environment variable containing the file path
|
|
41
|
+
* @returns The secret value, or undefined if neither is set
|
|
42
|
+
*/
|
|
43
|
+
export declare function getSecret(envVar: string, fileEnvVar: string): string | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* Validate hostname format.
|
|
46
|
+
* Accepts valid hostnames (RFC 1123) and IPv4 addresses.
|
|
47
|
+
*
|
|
48
|
+
* @param hostname - The hostname or IP address to validate
|
|
49
|
+
* @returns true if the hostname format is valid, false otherwise
|
|
50
|
+
*/
|
|
51
|
+
export declare function validateHostname(hostname: string): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Load configuration from environment variables.
|
|
54
|
+
*
|
|
55
|
+
* Supports file-based secrets for sensitive values (recommended for production):
|
|
56
|
+
* - DB2I_PASSWORD_FILE: Path to file containing password (e.g., Docker secret)
|
|
57
|
+
* - DB2I_USERNAME_FILE: Path to file containing username (optional)
|
|
58
|
+
*
|
|
59
|
+
* File-based secrets take priority over plain environment variables.
|
|
16
60
|
*/
|
|
17
61
|
export declare function loadConfig(): DB2iConfig;
|
|
18
62
|
/**
|
|
@@ -28,4 +72,54 @@ export declare function buildConnectionConfig(config: DB2iConfig): {
|
|
|
28
72
|
* Get the default schema from config
|
|
29
73
|
*/
|
|
30
74
|
export declare function getDefaultSchema(config: DB2iConfig): string | undefined;
|
|
75
|
+
/**
|
|
76
|
+
* Rate limit configuration interface
|
|
77
|
+
*/
|
|
78
|
+
export interface RateLimitConfig {
|
|
79
|
+
/** Time window in milliseconds (default: 900000 = 15 minutes) */
|
|
80
|
+
windowMs: number;
|
|
81
|
+
/** Maximum requests allowed per window (default: 100) */
|
|
82
|
+
maxRequests: number;
|
|
83
|
+
/** Whether rate limiting is enabled (default: true) */
|
|
84
|
+
enabled: boolean;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Default rate limit configuration values
|
|
88
|
+
*
|
|
89
|
+
* Environment variables:
|
|
90
|
+
* - RATE_LIMIT_WINDOW_MS: Time window in milliseconds (default: 900000)
|
|
91
|
+
* - RATE_LIMIT_MAX_REQUESTS: Max requests per window (default: 100)
|
|
92
|
+
* - RATE_LIMIT_ENABLED: Set to 'false' or '0' to disable (default: true)
|
|
93
|
+
*/
|
|
94
|
+
export declare const DEFAULT_RATE_LIMIT: RateLimitConfig;
|
|
95
|
+
/**
|
|
96
|
+
* Query limit configuration interface
|
|
97
|
+
*/
|
|
98
|
+
export interface QueryLimitConfig {
|
|
99
|
+
/** Default number of rows to return (default: 1000) */
|
|
100
|
+
defaultLimit: number;
|
|
101
|
+
/** Maximum number of rows allowed (default: 10000) */
|
|
102
|
+
maxLimit: number;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Default query limit configuration values
|
|
106
|
+
*
|
|
107
|
+
* Environment variables:
|
|
108
|
+
* - QUERY_DEFAULT_LIMIT: Default rows to return (default: 1000)
|
|
109
|
+
* - QUERY_MAX_LIMIT: Maximum rows allowed, caps user-provided limits (default: 10000)
|
|
110
|
+
*/
|
|
111
|
+
export declare const DEFAULT_QUERY_LIMIT: QueryLimitConfig;
|
|
112
|
+
/**
|
|
113
|
+
* Get query limit configuration from environment variables
|
|
114
|
+
*/
|
|
115
|
+
export declare function getQueryLimitConfig(): QueryLimitConfig;
|
|
116
|
+
/**
|
|
117
|
+
* Apply query limit constraints.
|
|
118
|
+
* Returns the effective limit, capped to maxLimit.
|
|
119
|
+
*
|
|
120
|
+
* @param requestedLimit - The limit requested by the user (or undefined for default)
|
|
121
|
+
* @param config - Query limit configuration
|
|
122
|
+
* @returns The effective limit to use
|
|
123
|
+
*/
|
|
124
|
+
export declare function applyQueryLimit(requestedLimit: number | undefined, config?: QueryLimitConfig): number;
|
|
31
125
|
//# sourceMappingURL=config.d.ts.map
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACrC;AAED;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAErE;;;GAGG;AACH,wBAAgB,WAAW,IAAI,QAAQ,CAStC;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAK3D;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAMhF;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAwB1D;AA6BD;;;;;;;;GAQG;AACH,wBAAgB,UAAU,IAAI,UAAU,CAiCvC;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,UAAU,GAAG;IACzD,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB,CA4BA;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,GAAG,SAAS,CAEvE;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,iEAAiE;IACjE,QAAQ,EAAE,MAAM,CAAC;IACjB,yDAAyD;IACzD,WAAW,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,EAAE,eAIhC,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,uDAAuD;IACvD,YAAY,EAAE,MAAM,CAAC;IACrB,sDAAsD;IACtD,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,EAAE,gBAGjC,CAAC;AAEF;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,gBAAgB,CAQtD;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,cAAc,EAAE,MAAM,GAAG,SAAS,EAClC,MAAM,GAAE,gBAAwC,GAC/C,MAAM,CAGR"}
|
package/dist/config.js
CHANGED
|
@@ -1,7 +1,82 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Configuration module for IBM DB2i MCP Server
|
|
3
3
|
* Handles environment variables and JDBC connection options
|
|
4
|
+
*
|
|
5
|
+
* Supports file-based secrets (e.g., Docker secrets) via *_FILE environment variables.
|
|
6
|
+
* File-based secrets take priority over plain environment variables.
|
|
4
7
|
*/
|
|
8
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
9
|
+
/**
|
|
10
|
+
* Get the configured log level from environment
|
|
11
|
+
* Defaults to 'info' if not set or invalid
|
|
12
|
+
*/
|
|
13
|
+
export function getLogLevel() {
|
|
14
|
+
const level = process.env.LOG_LEVEL?.toLowerCase();
|
|
15
|
+
const validLevels = ['debug', 'info', 'warn', 'error', 'fatal'];
|
|
16
|
+
if (level && validLevels.includes(level)) {
|
|
17
|
+
return level;
|
|
18
|
+
}
|
|
19
|
+
return 'info';
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Read a secret value from a file.
|
|
23
|
+
* Docker secrets are typically mounted at /run/secrets/<secret_name>
|
|
24
|
+
*
|
|
25
|
+
* @param filePath - Path to the file containing the secret
|
|
26
|
+
* @returns The secret value with leading/trailing whitespace trimmed
|
|
27
|
+
* @throws Error if file cannot be read
|
|
28
|
+
*/
|
|
29
|
+
export function readSecretFromFile(filePath) {
|
|
30
|
+
if (!existsSync(filePath)) {
|
|
31
|
+
throw new Error(`Secret file not found: ${filePath}`);
|
|
32
|
+
}
|
|
33
|
+
return readFileSync(filePath, 'utf8').trim();
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Get a secret value from either a file or environment variable.
|
|
37
|
+
* File-based secrets take priority (more secure).
|
|
38
|
+
*
|
|
39
|
+
* @param envVar - Name of the environment variable containing the value
|
|
40
|
+
* @param fileEnvVar - Name of the environment variable containing the file path
|
|
41
|
+
* @returns The secret value, or undefined if neither is set
|
|
42
|
+
*/
|
|
43
|
+
export function getSecret(envVar, fileEnvVar) {
|
|
44
|
+
const filePath = process.env[fileEnvVar];
|
|
45
|
+
if (filePath) {
|
|
46
|
+
return readSecretFromFile(filePath);
|
|
47
|
+
}
|
|
48
|
+
return process.env[envVar];
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Validate hostname format.
|
|
52
|
+
* Accepts valid hostnames (RFC 1123) and IPv4 addresses.
|
|
53
|
+
*
|
|
54
|
+
* @param hostname - The hostname or IP address to validate
|
|
55
|
+
* @returns true if the hostname format is valid, false otherwise
|
|
56
|
+
*/
|
|
57
|
+
export function validateHostname(hostname) {
|
|
58
|
+
const trimmed = hostname.trim();
|
|
59
|
+
if (!trimmed)
|
|
60
|
+
return false;
|
|
61
|
+
// IPv4 pattern: four octets separated by dots
|
|
62
|
+
const ipv4Pattern = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
|
|
63
|
+
const ipv4Match = trimmed.match(ipv4Pattern);
|
|
64
|
+
if (ipv4Match) {
|
|
65
|
+
// Validate each octet is 0-255
|
|
66
|
+
return ipv4Match.slice(1).every((octet) => {
|
|
67
|
+
const num = parseInt(octet, 10);
|
|
68
|
+
return num >= 0 && num <= 255;
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
// Hostname pattern (RFC 1123):
|
|
72
|
+
// - Labels separated by dots
|
|
73
|
+
// - Each label: 1-63 chars, alphanumeric or hyphen, cannot start/end with hyphen
|
|
74
|
+
// - Total length up to 253 chars
|
|
75
|
+
if (trimmed.length > 253)
|
|
76
|
+
return false;
|
|
77
|
+
const hostnamePattern = /^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
|
|
78
|
+
return hostnamePattern.test(trimmed);
|
|
79
|
+
}
|
|
5
80
|
/**
|
|
6
81
|
* Parse JDBC options from a semicolon-separated string
|
|
7
82
|
* Format: "key1=value1;key2=value2"
|
|
@@ -26,20 +101,29 @@ function parseJdbcOptions(optionsString) {
|
|
|
26
101
|
return options;
|
|
27
102
|
}
|
|
28
103
|
/**
|
|
29
|
-
* Load configuration from environment variables
|
|
104
|
+
* Load configuration from environment variables.
|
|
105
|
+
*
|
|
106
|
+
* Supports file-based secrets for sensitive values (recommended for production):
|
|
107
|
+
* - DB2I_PASSWORD_FILE: Path to file containing password (e.g., Docker secret)
|
|
108
|
+
* - DB2I_USERNAME_FILE: Path to file containing username (optional)
|
|
109
|
+
*
|
|
110
|
+
* File-based secrets take priority over plain environment variables.
|
|
30
111
|
*/
|
|
31
112
|
export function loadConfig() {
|
|
32
113
|
const hostname = process.env.DB2I_HOSTNAME;
|
|
33
|
-
const username =
|
|
34
|
-
const password =
|
|
114
|
+
const username = getSecret('DB2I_USERNAME', 'DB2I_USERNAME_FILE');
|
|
115
|
+
const password = getSecret('DB2I_PASSWORD', 'DB2I_PASSWORD_FILE');
|
|
35
116
|
if (!hostname) {
|
|
36
117
|
throw new Error('DB2I_HOSTNAME environment variable is required');
|
|
37
118
|
}
|
|
119
|
+
if (!validateHostname(hostname)) {
|
|
120
|
+
throw new Error(`Invalid DB2I_HOSTNAME format: "${hostname}". Must be a valid hostname or IPv4 address.`);
|
|
121
|
+
}
|
|
38
122
|
if (!username) {
|
|
39
|
-
throw new Error('DB2I_USERNAME environment variable is required');
|
|
123
|
+
throw new Error('DB2I_USERNAME environment variable is required (or DB2I_USERNAME_FILE for file-based secret)');
|
|
40
124
|
}
|
|
41
125
|
if (!password) {
|
|
42
|
-
throw new Error('DB2I_PASSWORD environment variable is required');
|
|
126
|
+
throw new Error('DB2I_PASSWORD environment variable is required (or DB2I_PASSWORD_FILE for file-based secret)');
|
|
43
127
|
}
|
|
44
128
|
return {
|
|
45
129
|
hostname,
|
|
@@ -80,4 +164,51 @@ export function buildConnectionConfig(config) {
|
|
|
80
164
|
export function getDefaultSchema(config) {
|
|
81
165
|
return config.schema || undefined;
|
|
82
166
|
}
|
|
167
|
+
/**
|
|
168
|
+
* Default rate limit configuration values
|
|
169
|
+
*
|
|
170
|
+
* Environment variables:
|
|
171
|
+
* - RATE_LIMIT_WINDOW_MS: Time window in milliseconds (default: 900000)
|
|
172
|
+
* - RATE_LIMIT_MAX_REQUESTS: Max requests per window (default: 100)
|
|
173
|
+
* - RATE_LIMIT_ENABLED: Set to 'false' or '0' to disable (default: true)
|
|
174
|
+
*/
|
|
175
|
+
export const DEFAULT_RATE_LIMIT = {
|
|
176
|
+
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
177
|
+
maxRequests: 100,
|
|
178
|
+
enabled: true,
|
|
179
|
+
};
|
|
180
|
+
/**
|
|
181
|
+
* Default query limit configuration values
|
|
182
|
+
*
|
|
183
|
+
* Environment variables:
|
|
184
|
+
* - QUERY_DEFAULT_LIMIT: Default rows to return (default: 1000)
|
|
185
|
+
* - QUERY_MAX_LIMIT: Maximum rows allowed, caps user-provided limits (default: 10000)
|
|
186
|
+
*/
|
|
187
|
+
export const DEFAULT_QUERY_LIMIT = {
|
|
188
|
+
defaultLimit: 1000,
|
|
189
|
+
maxLimit: 10000,
|
|
190
|
+
};
|
|
191
|
+
/**
|
|
192
|
+
* Get query limit configuration from environment variables
|
|
193
|
+
*/
|
|
194
|
+
export function getQueryLimitConfig() {
|
|
195
|
+
const defaultLimit = parseInt(process.env.QUERY_DEFAULT_LIMIT || '1000', 10);
|
|
196
|
+
const maxLimit = parseInt(process.env.QUERY_MAX_LIMIT || '10000', 10);
|
|
197
|
+
return {
|
|
198
|
+
defaultLimit: Math.max(1, defaultLimit),
|
|
199
|
+
maxLimit: Math.max(1, maxLimit),
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Apply query limit constraints.
|
|
204
|
+
* Returns the effective limit, capped to maxLimit.
|
|
205
|
+
*
|
|
206
|
+
* @param requestedLimit - The limit requested by the user (or undefined for default)
|
|
207
|
+
* @param config - Query limit configuration
|
|
208
|
+
* @returns The effective limit to use
|
|
209
|
+
*/
|
|
210
|
+
export function applyQueryLimit(requestedLimit, config = getQueryLimitConfig()) {
|
|
211
|
+
const limit = requestedLimit ?? config.defaultLimit;
|
|
212
|
+
return Math.min(Math.max(1, limit), config.maxLimit);
|
|
213
|
+
}
|
|
83
214
|
//# sourceMappingURL=config.js.map
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/db/connection.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* JDBC Connection Manager for IBM DB2i using node-jt400
|
|
3
3
|
*/
|
|
4
|
-
import { pool } from 'node-jt400';
|
|
5
4
|
import type { DB2iConfig } from '../config.js';
|
|
6
5
|
export interface QueryResult {
|
|
7
6
|
rows: Record<string, unknown>[];
|
|
@@ -19,24 +18,12 @@ export interface QueryResult {
|
|
|
19
18
|
* Initialize the connection pool
|
|
20
19
|
*/
|
|
21
20
|
export declare function initializePool(config: DB2iConfig): void;
|
|
22
|
-
/**
|
|
23
|
-
* Get the connection pool instance
|
|
24
|
-
*/
|
|
25
|
-
export declare function getPool(): ReturnType<typeof pool>;
|
|
26
21
|
/**
|
|
27
22
|
* Execute a query and return results
|
|
28
23
|
*/
|
|
29
24
|
export declare function executeQuery(sql: string, params?: unknown[]): Promise<QueryResult>;
|
|
30
|
-
/**
|
|
31
|
-
* Execute a query with metadata about columns
|
|
32
|
-
*/
|
|
33
|
-
export declare function executeQueryWithMetadata(sql: string, params?: unknown[]): Promise<QueryResult>;
|
|
34
25
|
/**
|
|
35
26
|
* Test the database connection
|
|
36
27
|
*/
|
|
37
28
|
export declare function testConnection(): Promise<boolean>;
|
|
38
|
-
/**
|
|
39
|
-
* Close the connection pool
|
|
40
|
-
*/
|
|
41
|
-
export declare function closePool(): Promise<void>;
|
|
42
29
|
//# sourceMappingURL=connection.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/db/connection.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/db/connection.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAM/C,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAChC,QAAQ,CAAC,EAAE;QACT,WAAW,EAAE,MAAM,CAAC;QACpB,OAAO,EAAE,KAAK,CAAC;YACb,IAAI,EAAE,MAAM,CAAC;YACb,IAAI,EAAE,MAAM,CAAC;YACb,SAAS,EAAE,MAAM,CAAC;YAClB,KAAK,EAAE,MAAM,CAAC;SACf,CAAC,CAAC;KACJ,CAAC;CACH;AAID;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAKvD;AA4BD;;GAEG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,EACX,MAAM,GAAE,OAAO,EAAO,GACrB,OAAO,CAAC,WAAW,CAAC,CAgBtB;AAED;;GAEG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CAUvD"}
|
package/dist/db/connection.js
CHANGED
|
@@ -3,18 +3,22 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { pool } from 'node-jt400';
|
|
5
5
|
import { buildConnectionConfig } from '../config.js';
|
|
6
|
+
import { createChildLogger } from '../utils/logger.js';
|
|
7
|
+
const log = createChildLogger({ component: 'database' });
|
|
6
8
|
let connectionPool = null;
|
|
7
9
|
/**
|
|
8
10
|
* Initialize the connection pool
|
|
9
11
|
*/
|
|
10
12
|
export function initializePool(config) {
|
|
13
|
+
log.debug({ hostname: config.hostname, port: config.port }, 'Initializing connection pool');
|
|
11
14
|
const connectionConfig = buildConnectionConfig(config);
|
|
12
15
|
connectionPool = pool(connectionConfig);
|
|
16
|
+
log.info({ hostname: config.hostname }, 'Connection pool created');
|
|
13
17
|
}
|
|
14
18
|
/**
|
|
15
|
-
* Get the connection pool instance
|
|
19
|
+
* Get the connection pool instance (internal use only)
|
|
16
20
|
*/
|
|
17
|
-
|
|
21
|
+
function getPool() {
|
|
18
22
|
if (!connectionPool) {
|
|
19
23
|
throw new Error('Connection pool not initialized. Call initializePool first.');
|
|
20
24
|
}
|
|
@@ -45,31 +49,16 @@ function toParams(params) {
|
|
|
45
49
|
export async function executeQuery(sql, params = []) {
|
|
46
50
|
const db = getPool();
|
|
47
51
|
try {
|
|
52
|
+
log.debug({ sql: sql.substring(0, 200), paramCount: params.length }, 'Executing query');
|
|
48
53
|
const typedParams = toParams(params);
|
|
49
54
|
const results = await db.query(sql, typedParams);
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
catch (error) {
|
|
55
|
-
const message = error instanceof Error ? error.message : 'Unknown database error';
|
|
56
|
-
throw new Error(`Database query failed: ${message}`);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Execute a query with metadata about columns
|
|
61
|
-
*/
|
|
62
|
-
export async function executeQueryWithMetadata(sql, params = []) {
|
|
63
|
-
const db = getPool();
|
|
64
|
-
try {
|
|
65
|
-
const typedParams = toParams(params);
|
|
66
|
-
const results = await db.query(sql, typedParams);
|
|
67
|
-
return {
|
|
68
|
-
rows: results,
|
|
69
|
-
};
|
|
55
|
+
const rows = results;
|
|
56
|
+
log.debug({ rowCount: rows.length }, 'Query completed');
|
|
57
|
+
return { rows };
|
|
70
58
|
}
|
|
71
59
|
catch (error) {
|
|
72
60
|
const message = error instanceof Error ? error.message : 'Unknown database error';
|
|
61
|
+
log.debug({ err: error, sql: sql.substring(0, 200) }, 'Database query failed');
|
|
73
62
|
throw new Error(`Database query failed: ${message}`);
|
|
74
63
|
}
|
|
75
64
|
}
|
|
@@ -78,20 +67,14 @@ export async function executeQueryWithMetadata(sql, params = []) {
|
|
|
78
67
|
*/
|
|
79
68
|
export async function testConnection() {
|
|
80
69
|
try {
|
|
70
|
+
log.debug('Testing database connection');
|
|
81
71
|
await executeQuery('SELECT 1 FROM SYSIBM.SYSDUMMY1');
|
|
72
|
+
log.debug('Connection test successful');
|
|
82
73
|
return true;
|
|
83
74
|
}
|
|
84
|
-
catch {
|
|
75
|
+
catch (error) {
|
|
76
|
+
log.warn({ err: error }, 'Connection test failed');
|
|
85
77
|
return false;
|
|
86
78
|
}
|
|
87
79
|
}
|
|
88
|
-
/**
|
|
89
|
-
* Close the connection pool
|
|
90
|
-
*/
|
|
91
|
-
export async function closePool() {
|
|
92
|
-
if (connectionPool) {
|
|
93
|
-
// node-jt400 pool doesn't have explicit close, but we can clear the reference
|
|
94
|
-
connectionPool = null;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
80
|
//# sourceMappingURL=connection.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/db/connection.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAGlC,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/db/connection.ts"],"names":[],"mappings":"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"}
|