mcp-server-db2i 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +252 -0
- package/dist/config.d.ts +31 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +83 -0
- package/dist/config.js.map +1 -0
- package/dist/db/connection.d.ts +42 -0
- package/dist/db/connection.d.ts.map +1 -0
- package/dist/db/connection.js +97 -0
- package/dist/db/connection.js.map +1 -0
- package/dist/db/queries.d.ts +75 -0
- package/dist/db/queries.d.ts.map +1 -0
- package/dist/db/queries.js +219 -0
- package/dist/db/queries.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +174 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/metadata.d.ts +216 -0
- package/dist/tools/metadata.d.ts.map +1 -0
- package/dist/tools/metadata.js +216 -0
- package/dist/tools/metadata.js.map +1 -0
- package/dist/tools/query.d.ts +36 -0
- package/dist/tools/query.d.ts.map +1 -0
- package/dist/tools/query.js +62 -0
- package/dist/tools/query.js.map +1 -0
- package/package.json +58 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ström Capital
|
|
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
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# mcp-server-db2i
|
|
2
|
+
|
|
3
|
+
A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server for IBM DB2 for i (DB2i). This server enables AI assistants like Claude and Cursor to query and inspect IBM i databases using the JT400 JDBC driver.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Read-only SQL queries** - Execute SELECT statements safely with automatic result limiting
|
|
8
|
+
- **Schema inspection** - List all schemas/libraries with optional filtering
|
|
9
|
+
- **Table metadata** - List tables, describe columns, view indexes and constraints
|
|
10
|
+
- **View inspection** - List and explore database views
|
|
11
|
+
- **Secure by design** - Only SELECT queries allowed, credentials via environment variables
|
|
12
|
+
- **Docker support** - Run as a container for easy deployment
|
|
13
|
+
|
|
14
|
+
## Available Tools
|
|
15
|
+
|
|
16
|
+
| Tool | Description |
|
|
17
|
+
|------|-------------|
|
|
18
|
+
| `execute_query` | Execute read-only SELECT queries |
|
|
19
|
+
| `list_schemas` | List schemas/libraries (with optional filter) |
|
|
20
|
+
| `list_tables` | List tables in a schema (with optional filter) |
|
|
21
|
+
| `describe_table` | Get detailed column information |
|
|
22
|
+
| `list_views` | List views in a schema (with optional filter) |
|
|
23
|
+
| `list_indexes` | List indexes for a table |
|
|
24
|
+
| `get_table_constraints` | Get primary keys, foreign keys, unique constraints |
|
|
25
|
+
|
|
26
|
+
### Filter Syntax
|
|
27
|
+
|
|
28
|
+
The list tools support pattern matching:
|
|
29
|
+
- `CUST` - Contains "CUST"
|
|
30
|
+
- `CUST*` - Starts with "CUST"
|
|
31
|
+
- `*LOG` - Ends with "LOG"
|
|
32
|
+
- `ORD*FILE` - Starts with "ORD", ends with "FILE"
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
### Prerequisites
|
|
37
|
+
|
|
38
|
+
- Node.js 18 or higher
|
|
39
|
+
- Java Runtime Environment (JRE) 11 or higher (for JDBC)
|
|
40
|
+
- Access to an IBM i system
|
|
41
|
+
|
|
42
|
+
### Option 1: npm (recommended)
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npm install -g mcp-server-db2i
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Option 2: From source
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
git clone https://github.com/Strom-Capital/mcp-server-db2i.git
|
|
52
|
+
cd mcp-server-db2i
|
|
53
|
+
npm install
|
|
54
|
+
npm run build
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Option 3: Docker
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
docker build -t mcp-server-db2i .
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Configuration
|
|
64
|
+
|
|
65
|
+
Create a `.env` file or set environment variables:
|
|
66
|
+
|
|
67
|
+
```env
|
|
68
|
+
# Required
|
|
69
|
+
DB2I_HOSTNAME=your-ibm-i-host.com
|
|
70
|
+
DB2I_USERNAME=your-username
|
|
71
|
+
DB2I_PASSWORD=your-password
|
|
72
|
+
|
|
73
|
+
# Optional
|
|
74
|
+
DB2I_PORT=446 # Default: 446
|
|
75
|
+
DB2I_DATABASE=*LOCAL # Default: *LOCAL
|
|
76
|
+
DB2I_SCHEMA=your-default-schema # Default schema for all tools (can be overridden per-call)
|
|
77
|
+
DB2I_JDBC_OPTIONS=naming=system;date format=iso
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Environment Variables
|
|
81
|
+
|
|
82
|
+
| Variable | Required | Default | Description |
|
|
83
|
+
|----------|----------|---------|-------------|
|
|
84
|
+
| `DB2I_HOSTNAME` | Yes | - | IBM i hostname or IP address |
|
|
85
|
+
| `DB2I_USERNAME` | Yes | - | IBM i user profile |
|
|
86
|
+
| `DB2I_PASSWORD` | Yes | - | User password |
|
|
87
|
+
| `DB2I_PORT` | No | `446` | JDBC port (446 is standard for IBM i) |
|
|
88
|
+
| `DB2I_DATABASE` | No | `*LOCAL` | Database name |
|
|
89
|
+
| `DB2I_SCHEMA` | No | - | Default schema/library for tools. If set, you don't need to specify schema in each tool call. |
|
|
90
|
+
| `DB2I_JDBC_OPTIONS` | No | - | Additional JDBC options (semicolon-separated) |
|
|
91
|
+
|
|
92
|
+
### JDBC Options
|
|
93
|
+
|
|
94
|
+
Common JDBC options for IBM i (JT400/JTOpen driver):
|
|
95
|
+
|
|
96
|
+
| Option | Values | Description |
|
|
97
|
+
|--------|--------|-------------|
|
|
98
|
+
| `naming` | `system`, `sql` | `system` uses `/` for library separator, `sql` uses `.` for schema separator |
|
|
99
|
+
| `libraries` | `LIB1,LIB2,...` | Library list for resolving unqualified names |
|
|
100
|
+
| `date format` | `iso`, `usa`, `eur`, `jis`, `mdy`, `dmy`, `ymd` | Date format for date fields |
|
|
101
|
+
| `time format` | `iso`, `usa`, `eur`, `jis`, `hms` | Time format for time fields |
|
|
102
|
+
| `errors` | `full`, `basic` | Level of detail in error messages (`full` helps debugging) |
|
|
103
|
+
| `translate binary` | `true`, `false` | Whether to translate binary/CCSID data |
|
|
104
|
+
| `secure` | `true`, `false` | Enable SSL/TLS encryption |
|
|
105
|
+
|
|
106
|
+
Example: `naming=system;date format=iso;errors=full`
|
|
107
|
+
|
|
108
|
+
## Usage with Cursor
|
|
109
|
+
|
|
110
|
+
Add to your Cursor MCP settings (`~/.cursor/mcp.json`):
|
|
111
|
+
|
|
112
|
+
### Using Docker (recommended)
|
|
113
|
+
|
|
114
|
+
```json
|
|
115
|
+
{
|
|
116
|
+
"mcpServers": {
|
|
117
|
+
"db2i": {
|
|
118
|
+
"command": "docker",
|
|
119
|
+
"args": [
|
|
120
|
+
"run", "-i", "--rm",
|
|
121
|
+
"-e", "DB2I_HOSTNAME=your-host",
|
|
122
|
+
"-e", "DB2I_USERNAME=your-user",
|
|
123
|
+
"-e", "DB2I_PASSWORD=your-password",
|
|
124
|
+
"mcp-server-db2i:latest"
|
|
125
|
+
]
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Using Docker with env file
|
|
132
|
+
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"mcpServers": {
|
|
136
|
+
"db2i": {
|
|
137
|
+
"command": "docker",
|
|
138
|
+
"args": [
|
|
139
|
+
"run", "-i", "--rm",
|
|
140
|
+
"--env-file", "/path/to/your/.env",
|
|
141
|
+
"mcp-server-db2i:latest"
|
|
142
|
+
]
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Using docker-compose
|
|
149
|
+
|
|
150
|
+
Create a `.env` file in the project root, then:
|
|
151
|
+
|
|
152
|
+
```json
|
|
153
|
+
{
|
|
154
|
+
"mcpServers": {
|
|
155
|
+
"db2i": {
|
|
156
|
+
"command": "docker-compose",
|
|
157
|
+
"args": ["run", "--rm", "mcp-server-db2i"],
|
|
158
|
+
"cwd": "/path/to/mcp-server-db2i"
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
The `docker-compose.yml` automatically reads from `.env` in the same directory.
|
|
165
|
+
|
|
166
|
+
### Using npx (after npm install)
|
|
167
|
+
|
|
168
|
+
```json
|
|
169
|
+
{
|
|
170
|
+
"mcpServers": {
|
|
171
|
+
"db2i": {
|
|
172
|
+
"command": "npx",
|
|
173
|
+
"args": ["mcp-server-db2i"],
|
|
174
|
+
"env": {
|
|
175
|
+
"DB2I_HOSTNAME": "your-host",
|
|
176
|
+
"DB2I_USERNAME": "your-user",
|
|
177
|
+
"DB2I_PASSWORD": "your-password"
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Local development
|
|
185
|
+
|
|
186
|
+
```json
|
|
187
|
+
{
|
|
188
|
+
"mcpServers": {
|
|
189
|
+
"db2i": {
|
|
190
|
+
"command": "npx",
|
|
191
|
+
"args": ["tsx", "/path/to/mcp-server-db2i/src/index.ts"],
|
|
192
|
+
"env": {
|
|
193
|
+
"DB2I_HOSTNAME": "your-host",
|
|
194
|
+
"DB2I_USERNAME": "your-user",
|
|
195
|
+
"DB2I_PASSWORD": "your-password"
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Example Queries
|
|
203
|
+
|
|
204
|
+
Once connected, you can ask the AI assistant:
|
|
205
|
+
|
|
206
|
+
- "List all schemas that contain 'PROD'"
|
|
207
|
+
- "Show me the tables in schema MYLIB"
|
|
208
|
+
- "Describe the columns in MYLIB/CUSTOMERS"
|
|
209
|
+
- "What indexes exist on the ORDERS table?"
|
|
210
|
+
- "Run this query: SELECT * FROM MYLIB.CUSTOMERS WHERE STATUS = 'A'"
|
|
211
|
+
|
|
212
|
+
## Development
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
# Install dependencies
|
|
216
|
+
npm install
|
|
217
|
+
|
|
218
|
+
# Run in development mode
|
|
219
|
+
npm run dev
|
|
220
|
+
|
|
221
|
+
# Build for production
|
|
222
|
+
npm run build
|
|
223
|
+
|
|
224
|
+
# Run production build
|
|
225
|
+
npm start
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Security
|
|
229
|
+
|
|
230
|
+
- **Read-only access**: Only SELECT statements are permitted
|
|
231
|
+
- **No credentials in code**: All sensitive data via environment variables
|
|
232
|
+
- **Query validation**: Dangerous SQL keywords are blocked
|
|
233
|
+
- **Result limiting**: Default limit of 1000 rows prevents large result sets
|
|
234
|
+
|
|
235
|
+
## Compatibility
|
|
236
|
+
|
|
237
|
+
- IBM i V7R3 and later (V7R5 recommended)
|
|
238
|
+
- Works with any IBM i system accessible via JDBC over TCP/IP
|
|
239
|
+
|
|
240
|
+
## Contributing
|
|
241
|
+
|
|
242
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
243
|
+
|
|
244
|
+
## License
|
|
245
|
+
|
|
246
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
247
|
+
|
|
248
|
+
## Acknowledgments
|
|
249
|
+
|
|
250
|
+
- [node-jt400](https://www.npmjs.com/package/node-jt400) - JT400 JDBC driver wrapper for Node.js
|
|
251
|
+
- [Model Context Protocol](https://modelcontextprotocol.io/) - The protocol specification
|
|
252
|
+
- [@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/typescript-sdk) - Official TypeScript SDK
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration module for IBM DB2i MCP Server
|
|
3
|
+
* Handles environment variables and JDBC connection options
|
|
4
|
+
*/
|
|
5
|
+
export interface DB2iConfig {
|
|
6
|
+
hostname: string;
|
|
7
|
+
port: number;
|
|
8
|
+
username: string;
|
|
9
|
+
password: string;
|
|
10
|
+
database: string;
|
|
11
|
+
schema: string;
|
|
12
|
+
jdbcOptions: Record<string, string>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Load configuration from environment variables
|
|
16
|
+
*/
|
|
17
|
+
export declare function loadConfig(): DB2iConfig;
|
|
18
|
+
/**
|
|
19
|
+
* Build JDBC connection configuration for node-jt400
|
|
20
|
+
*/
|
|
21
|
+
export declare function buildConnectionConfig(config: DB2iConfig): {
|
|
22
|
+
host: string;
|
|
23
|
+
user: string;
|
|
24
|
+
password: string;
|
|
25
|
+
[key: string]: string;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Get the default schema from config
|
|
29
|
+
*/
|
|
30
|
+
export declare function getDefaultSchema(config: DB2iConfig): string | undefined;
|
|
31
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,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;AA6BD;;GAEG;AACH,wBAAgB,UAAU,IAAI,UAAU,CAwBvC;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"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration module for IBM DB2i MCP Server
|
|
3
|
+
* Handles environment variables and JDBC connection options
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Parse JDBC options from a semicolon-separated string
|
|
7
|
+
* Format: "key1=value1;key2=value2"
|
|
8
|
+
*/
|
|
9
|
+
function parseJdbcOptions(optionsString) {
|
|
10
|
+
if (!optionsString) {
|
|
11
|
+
return {};
|
|
12
|
+
}
|
|
13
|
+
const options = {};
|
|
14
|
+
const pairs = optionsString.split(';');
|
|
15
|
+
for (const pair of pairs) {
|
|
16
|
+
const trimmed = pair.trim();
|
|
17
|
+
if (!trimmed)
|
|
18
|
+
continue;
|
|
19
|
+
const eqIndex = trimmed.indexOf('=');
|
|
20
|
+
if (eqIndex > 0) {
|
|
21
|
+
const key = trimmed.substring(0, eqIndex).trim();
|
|
22
|
+
const value = trimmed.substring(eqIndex + 1).trim();
|
|
23
|
+
options[key] = value;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return options;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Load configuration from environment variables
|
|
30
|
+
*/
|
|
31
|
+
export function loadConfig() {
|
|
32
|
+
const hostname = process.env.DB2I_HOSTNAME;
|
|
33
|
+
const username = process.env.DB2I_USERNAME;
|
|
34
|
+
const password = process.env.DB2I_PASSWORD;
|
|
35
|
+
if (!hostname) {
|
|
36
|
+
throw new Error('DB2I_HOSTNAME environment variable is required');
|
|
37
|
+
}
|
|
38
|
+
if (!username) {
|
|
39
|
+
throw new Error('DB2I_USERNAME environment variable is required');
|
|
40
|
+
}
|
|
41
|
+
if (!password) {
|
|
42
|
+
throw new Error('DB2I_PASSWORD environment variable is required');
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
hostname,
|
|
46
|
+
port: parseInt(process.env.DB2I_PORT || '446', 10),
|
|
47
|
+
username,
|
|
48
|
+
password,
|
|
49
|
+
database: process.env.DB2I_DATABASE || '*LOCAL',
|
|
50
|
+
schema: process.env.DB2I_SCHEMA || '',
|
|
51
|
+
jdbcOptions: parseJdbcOptions(process.env.DB2I_JDBC_OPTIONS),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Build JDBC connection configuration for node-jt400
|
|
56
|
+
*/
|
|
57
|
+
export function buildConnectionConfig(config) {
|
|
58
|
+
const connectionConfig = {
|
|
59
|
+
host: config.hostname,
|
|
60
|
+
user: config.username,
|
|
61
|
+
password: config.password,
|
|
62
|
+
};
|
|
63
|
+
// Add default naming convention (system naming uses / for library separator)
|
|
64
|
+
if (!config.jdbcOptions['naming']) {
|
|
65
|
+
connectionConfig['naming'] = 'system';
|
|
66
|
+
}
|
|
67
|
+
// Add date format if not specified
|
|
68
|
+
if (!config.jdbcOptions['date format']) {
|
|
69
|
+
connectionConfig['date format'] = 'iso';
|
|
70
|
+
}
|
|
71
|
+
// Merge additional JDBC options
|
|
72
|
+
for (const [key, value] of Object.entries(config.jdbcOptions)) {
|
|
73
|
+
connectionConfig[key] = value;
|
|
74
|
+
}
|
|
75
|
+
return connectionConfig;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Get the default schema from config
|
|
79
|
+
*/
|
|
80
|
+
export function getDefaultSchema(config) {
|
|
81
|
+
return config.schema || undefined;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JDBC Connection Manager for IBM DB2i using node-jt400
|
|
3
|
+
*/
|
|
4
|
+
import { pool } from 'node-jt400';
|
|
5
|
+
import type { DB2iConfig } from '../config.js';
|
|
6
|
+
export interface QueryResult {
|
|
7
|
+
rows: Record<string, unknown>[];
|
|
8
|
+
metadata?: {
|
|
9
|
+
columnCount: number;
|
|
10
|
+
columns: Array<{
|
|
11
|
+
name: string;
|
|
12
|
+
type: string;
|
|
13
|
+
precision: number;
|
|
14
|
+
scale: number;
|
|
15
|
+
}>;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Initialize the connection pool
|
|
20
|
+
*/
|
|
21
|
+
export declare function initializePool(config: DB2iConfig): void;
|
|
22
|
+
/**
|
|
23
|
+
* Get the connection pool instance
|
|
24
|
+
*/
|
|
25
|
+
export declare function getPool(): ReturnType<typeof pool>;
|
|
26
|
+
/**
|
|
27
|
+
* Execute a query and return results
|
|
28
|
+
*/
|
|
29
|
+
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
|
+
/**
|
|
35
|
+
* Test the database connection
|
|
36
|
+
*/
|
|
37
|
+
export declare function testConnection(): Promise<boolean>;
|
|
38
|
+
/**
|
|
39
|
+
* Close the connection pool
|
|
40
|
+
*/
|
|
41
|
+
export declare function closePool(): Promise<void>;
|
|
42
|
+
//# sourceMappingURL=connection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/db/connection.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAElC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG/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,CAGvD;AAED;;GAEG;AACH,wBAAgB,OAAO,IAAI,UAAU,CAAC,OAAO,IAAI,CAAC,CAKjD;AAkBD;;GAEG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,EACX,MAAM,GAAE,OAAO,EAAO,GACrB,OAAO,CAAC,WAAW,CAAC,CActB;AAED;;GAEG;AACH,wBAAsB,wBAAwB,CAC5C,GAAG,EAAE,MAAM,EACX,MAAM,GAAE,OAAO,EAAO,GACrB,OAAO,CAAC,WAAW,CAAC,CActB;AAED;;GAEG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CAOvD;AAED;;GAEG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAK/C"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JDBC Connection Manager for IBM DB2i using node-jt400
|
|
3
|
+
*/
|
|
4
|
+
import { pool } from 'node-jt400';
|
|
5
|
+
import { buildConnectionConfig } from '../config.js';
|
|
6
|
+
let connectionPool = null;
|
|
7
|
+
/**
|
|
8
|
+
* Initialize the connection pool
|
|
9
|
+
*/
|
|
10
|
+
export function initializePool(config) {
|
|
11
|
+
const connectionConfig = buildConnectionConfig(config);
|
|
12
|
+
connectionPool = pool(connectionConfig);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Get the connection pool instance
|
|
16
|
+
*/
|
|
17
|
+
export function getPool() {
|
|
18
|
+
if (!connectionPool) {
|
|
19
|
+
throw new Error('Connection pool not initialized. Call initializePool first.');
|
|
20
|
+
}
|
|
21
|
+
return connectionPool;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Convert unknown params to Param type, filtering out undefined
|
|
25
|
+
*/
|
|
26
|
+
function toParams(params) {
|
|
27
|
+
return params
|
|
28
|
+
.filter((p) => p !== undefined)
|
|
29
|
+
.map((p) => {
|
|
30
|
+
if (p === null)
|
|
31
|
+
return null;
|
|
32
|
+
if (typeof p === 'string')
|
|
33
|
+
return p;
|
|
34
|
+
if (typeof p === 'number')
|
|
35
|
+
return p;
|
|
36
|
+
if (p instanceof Date)
|
|
37
|
+
return p;
|
|
38
|
+
// Convert other types to string
|
|
39
|
+
return String(p);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Execute a query and return results
|
|
44
|
+
*/
|
|
45
|
+
export async function executeQuery(sql, params = []) {
|
|
46
|
+
const db = getPool();
|
|
47
|
+
try {
|
|
48
|
+
const typedParams = toParams(params);
|
|
49
|
+
const results = await db.query(sql, typedParams);
|
|
50
|
+
return {
|
|
51
|
+
rows: results,
|
|
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
|
+
};
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
const message = error instanceof Error ? error.message : 'Unknown database error';
|
|
73
|
+
throw new Error(`Database query failed: ${message}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Test the database connection
|
|
78
|
+
*/
|
|
79
|
+
export async function testConnection() {
|
|
80
|
+
try {
|
|
81
|
+
await executeQuery('SELECT 1 FROM SYSIBM.SYSDUMMY1');
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
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
|
+
//# sourceMappingURL=connection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/db/connection.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Query helpers for IBM DB2i metadata and data access
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Convert a filter pattern to SQL LIKE pattern
|
|
6
|
+
* Supports:
|
|
7
|
+
* - "term" -> "%term%" (contains)
|
|
8
|
+
* - "term*" -> "term%" (starts with)
|
|
9
|
+
* - "*term" -> "%term" (ends with)
|
|
10
|
+
* - "term*suffix" -> "term%suffix" (pattern)
|
|
11
|
+
*/
|
|
12
|
+
export declare function filterToLikePattern(filter: string | undefined): string;
|
|
13
|
+
/**
|
|
14
|
+
* Validate that a query is read-only (SELECT only)
|
|
15
|
+
*/
|
|
16
|
+
export declare function isReadOnlyQuery(sql: string): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* List all schemas/libraries
|
|
19
|
+
*/
|
|
20
|
+
export declare function listSchemas(filter?: string): Promise<Array<{
|
|
21
|
+
schema_name: string;
|
|
22
|
+
schema_text: string | null;
|
|
23
|
+
}>>;
|
|
24
|
+
/**
|
|
25
|
+
* List tables in a schema
|
|
26
|
+
*/
|
|
27
|
+
export declare function listTables(schema: string, filter?: string): Promise<Array<{
|
|
28
|
+
table_name: string;
|
|
29
|
+
table_type: string;
|
|
30
|
+
table_text: string | null;
|
|
31
|
+
}>>;
|
|
32
|
+
/**
|
|
33
|
+
* Describe a table's columns
|
|
34
|
+
*/
|
|
35
|
+
export declare function describeTable(schema: string, table: string): Promise<Array<{
|
|
36
|
+
column_name: string;
|
|
37
|
+
ordinal_position: number;
|
|
38
|
+
data_type: string;
|
|
39
|
+
length: number | null;
|
|
40
|
+
numeric_scale: number | null;
|
|
41
|
+
is_nullable: string;
|
|
42
|
+
column_default: string | null;
|
|
43
|
+
column_text: string | null;
|
|
44
|
+
system_column_name: string;
|
|
45
|
+
ccsid: number | null;
|
|
46
|
+
}>>;
|
|
47
|
+
/**
|
|
48
|
+
* List views in a schema
|
|
49
|
+
*/
|
|
50
|
+
export declare function listViews(schema: string, filter?: string): Promise<Array<{
|
|
51
|
+
view_name: string;
|
|
52
|
+
view_text: string | null;
|
|
53
|
+
}>>;
|
|
54
|
+
/**
|
|
55
|
+
* List indexes for a table
|
|
56
|
+
*/
|
|
57
|
+
export declare function listIndexes(schema: string, table: string): Promise<Array<{
|
|
58
|
+
index_name: string;
|
|
59
|
+
index_schema: string;
|
|
60
|
+
is_unique: string;
|
|
61
|
+
column_names: string;
|
|
62
|
+
}>>;
|
|
63
|
+
/**
|
|
64
|
+
* Get table constraints (primary keys, foreign keys, unique constraints)
|
|
65
|
+
*/
|
|
66
|
+
export declare function getTableConstraints(schema: string, table: string): Promise<Array<{
|
|
67
|
+
constraint_name: string;
|
|
68
|
+
constraint_type: string;
|
|
69
|
+
column_name: string;
|
|
70
|
+
ordinal_position: number;
|
|
71
|
+
referenced_table_schema: string | null;
|
|
72
|
+
referenced_table_name: string | null;
|
|
73
|
+
referenced_column_name: string | null;
|
|
74
|
+
}>>;
|
|
75
|
+
//# sourceMappingURL=queries.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../../src/db/queries.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAiBtE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAgCpD;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAAC,CAkBtH;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,KAAK,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAAC,CAqBvF;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,KAAK,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB,CAAC,CAAC,CAiCF;AAED;;GAEG;AACH,wBAAsB,SAAS,CAC7B,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,KAAK,CAAC;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAAC,CAoBjE;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,KAAK,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC,CAAC,CAqBF;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,KAAK,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,uBAAuB,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC,CAAC,CAAC,CAqCF"}
|