storage-map-mcp-server 0.1.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 (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +265 -0
  3. package/build/clients/MySqlClient.d.ts +14 -0
  4. package/build/clients/MySqlClient.d.ts.map +1 -0
  5. package/build/clients/MySqlClient.js +141 -0
  6. package/build/clients/MySqlClient.js.map +1 -0
  7. package/build/clients/athena-client/AthenaClient.d.ts +17 -0
  8. package/build/clients/athena-client/AthenaClient.d.ts.map +1 -0
  9. package/build/clients/athena-client/AthenaClient.js +199 -0
  10. package/build/clients/athena-client/AthenaClient.js.map +1 -0
  11. package/build/clients/athena-client/index.d.ts +2 -0
  12. package/build/clients/athena-client/index.d.ts.map +1 -0
  13. package/build/clients/athena-client/index.js +2 -0
  14. package/build/clients/athena-client/index.js.map +1 -0
  15. package/build/clients/index.d.ts +3 -0
  16. package/build/clients/index.d.ts.map +1 -0
  17. package/build/clients/index.js +3 -0
  18. package/build/clients/index.js.map +1 -0
  19. package/build/clients/mysql-client/MySqlClient.d.ts +14 -0
  20. package/build/clients/mysql-client/MySqlClient.d.ts.map +1 -0
  21. package/build/clients/mysql-client/MySqlClient.js +141 -0
  22. package/build/clients/mysql-client/MySqlClient.js.map +1 -0
  23. package/build/clients/mysql-client/index.d.ts +2 -0
  24. package/build/clients/mysql-client/index.d.ts.map +1 -0
  25. package/build/clients/mysql-client/index.js +2 -0
  26. package/build/clients/mysql-client/index.js.map +1 -0
  27. package/build/clients/mysql.d.ts +14 -0
  28. package/build/clients/mysql.d.ts.map +1 -0
  29. package/build/clients/mysql.js +68 -0
  30. package/build/clients/mysql.js.map +1 -0
  31. package/build/config.d.ts +20 -0
  32. package/build/config.d.ts.map +1 -0
  33. package/build/config.js +80 -0
  34. package/build/config.js.map +1 -0
  35. package/build/index.d.ts +2 -0
  36. package/build/index.d.ts.map +1 -0
  37. package/build/index.js +10 -0
  38. package/build/index.js.map +1 -0
  39. package/build/mcp-connectors/McpConnector.d.ts +14 -0
  40. package/build/mcp-connectors/McpConnector.d.ts.map +1 -0
  41. package/build/mcp-connectors/McpConnector.js +2 -0
  42. package/build/mcp-connectors/McpConnector.js.map +1 -0
  43. package/build/mcp-connectors/McpConnectorRegistry.d.ts +17 -0
  44. package/build/mcp-connectors/McpConnectorRegistry.d.ts.map +1 -0
  45. package/build/mcp-connectors/McpConnectorRegistry.js +46 -0
  46. package/build/mcp-connectors/McpConnectorRegistry.js.map +1 -0
  47. package/build/mcp-connectors/index.d.ts +3 -0
  48. package/build/mcp-connectors/index.d.ts.map +1 -0
  49. package/build/mcp-connectors/index.js +2 -0
  50. package/build/mcp-connectors/index.js.map +1 -0
  51. package/build/server.d.ts +2 -0
  52. package/build/server.d.ts.map +1 -0
  53. package/build/server.js +172 -0
  54. package/build/server.js.map +1 -0
  55. package/package.json +73 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Hoon
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,265 @@
1
+ # MCP Storage Map
2
+
3
+ ![License](https://img.shields.io/github/license/cyhoon/mcp-storage-map)
4
+ ![Node Version](https://img.shields.io/badge/node-%3E%3D24.13.1-brightgreen)
5
+
6
+ Model Context Protocol (MCP) server for unified database and storage access. Query multiple databases (MySQL, AWS Athena, and more) through a single, consistent interface.
7
+
8
+ ## Quick Start
9
+
10
+ ### 1. Installation
11
+
12
+ ```bash
13
+ pnpm add -g @storage-map/mcp-server
14
+ ```
15
+
16
+ Or run directly with pnpm dlx:
17
+ ```bash
18
+ pnpm dlx @storage-map/mcp-server
19
+ ```
20
+
21
+ ### 2. Configure Your IDE
22
+
23
+ Add to your Claude Desktop, Claude Code, or Cursor MCP configuration.
24
+
25
+ **Single Database Example:**
26
+
27
+ ```json
28
+ {
29
+ "mcpServers": {
30
+ "storage-map": {
31
+ "command": "pnpm",
32
+ "args": ["dlx", "@storage-map/mcp-server"],
33
+ "env": {
34
+ "STORAGE_MYSQL_TYPE": "mysql",
35
+ "STORAGE_MYSQL_HOST": "localhost",
36
+ "STORAGE_MYSQL_PORT": "3306",
37
+ "STORAGE_MYSQL_USER": "root",
38
+ "STORAGE_MYSQL_PASSWORD": "password",
39
+ "STORAGE_MYSQL_DATABASE": "myapp",
40
+ "STORAGE_MYSQL_WRITE_MODE": "false"
41
+ }
42
+ }
43
+ }
44
+ }
45
+ ```
46
+
47
+ **Multiple Databases Example:**
48
+
49
+ ```json
50
+ {
51
+ "mcpServers": {
52
+ "storage-map": {
53
+ "command": "pnpm",
54
+ "args": ["dlx", "@storage-map/mcp-server"],
55
+ "env": {
56
+ "STORAGE_PROD_TYPE": "mysql",
57
+ "STORAGE_PROD_HOST": "prod-db.example.com",
58
+ "STORAGE_PROD_PORT": "3306",
59
+ "STORAGE_PROD_USER": "readonly",
60
+ "STORAGE_PROD_PASSWORD": "secret",
61
+ "STORAGE_PROD_DATABASE": "production",
62
+
63
+ "STORAGE_STAGING_TYPE": "mysql",
64
+ "STORAGE_STAGING_HOST": "staging-db.example.com",
65
+ "STORAGE_STAGING_PORT": "3306",
66
+ "STORAGE_STAGING_USER": "admin",
67
+ "STORAGE_STAGING_PASSWORD": "secret",
68
+ "STORAGE_STAGING_DATABASE": "staging",
69
+ "STORAGE_STAGING_WRITE_MODE": "true",
70
+
71
+ "STORAGE_ANALYTICS_TYPE": "athena",
72
+ "STORAGE_ANALYTICS_REGION": "us-west-2",
73
+ "STORAGE_ANALYTICS_DATABASE": "analytics",
74
+ "STORAGE_ANALYTICS_S3_OUTPUT_LOCATION": "s3://analytics-results/"
75
+ }
76
+ }
77
+ }
78
+ }
79
+ ```
80
+
81
+ > **Tip**: Each database connection needs a unique ID (the part after `STORAGE_` and before the property name). In the examples above: `MYSQL`, `PROD`, `STAGING`, and `ANALYTICS` are the IDs.
82
+
83
+ ### 3. Start Using
84
+
85
+ Ask your AI assistant to:
86
+ - **"List all my configured databases"**
87
+ - **"Query the users table from my MySQL database"**
88
+ - **"Show me tables in my Athena database"**
89
+ - **"Get the schema for the orders table"**
90
+ - **"Execute SELECT * FROM products LIMIT 10 on mysql storage"**
91
+
92
+ ## Compatibility
93
+
94
+ | Database | Status | Version |
95
+ |----------|--------|---------|
96
+ | MySQL | ✅ Fully supported | 5.7+ |
97
+ | AWS Athena | ✅ Fully supported | All versions |
98
+
99
+ ## Key Tools
100
+
101
+ | Tool | Description |
102
+ |------|-------------|
103
+ | `list_storages` | List all configured storage connections |
104
+ | `query` | Execute read queries (SELECT, etc.) |
105
+ | `execute` | Execute write operations (INSERT, UPDATE, DELETE) |
106
+ | `list_collections` | List tables/collections in a storage |
107
+ | `describe_collection` | Get schema information for a table/collection |
108
+ | `get_storage_info` | Get detailed connection information |
109
+
110
+ ## Configuration
111
+
112
+ ### Environment Variables
113
+
114
+ Storage Map uses a flexible configuration system with environment variables:
115
+
116
+ **Pattern**: `STORAGE_<ID>_<PROPERTY>`
117
+
118
+ - `<ID>`: Unique identifier for your storage connection (e.g., `MYSQL`, `PROD`, `STAGING`, `ANALYTICS`)
119
+ - Use descriptive names to identify each connection
120
+ - Same database type can have multiple IDs (e.g., `PROD_DB` and `STAGING_DB` for different MySQL servers)
121
+ - `<PROPERTY>`: Configuration property (e.g., `TYPE`, `HOST`, `PORT`)
122
+
123
+ **Common Properties**:
124
+ - `TYPE`: Database type (`mysql`, `athena`) - **Required**
125
+ - `WRITE_MODE`: Enable write operations (`true` or `false`, default: `false`)
126
+
127
+ **MySQL**:
128
+ - `HOST`: Database host - **Required**
129
+ - `PORT`: Database port (default: `3306`)
130
+ - `USER`: Username - **Required**
131
+ - `PASSWORD`: Password - **Required**
132
+ - `DATABASE`: Database name - **Required**
133
+
134
+ **AWS Athena**:
135
+ - `REGION`: AWS region (default: `us-east-1`)
136
+ - `DATABASE`: Athena database/catalog (default: `default`)
137
+ - `S3_OUTPUT_LOCATION`: S3 path for query results - **Required**
138
+ - `WORKGROUP`: Athena workgroup (default: `primary`)
139
+
140
+ ### Configuration Examples
141
+
142
+ **Example 1: Single MySQL Database**
143
+
144
+ ```bash
145
+ STORAGE_MYSQL_TYPE=mysql
146
+ STORAGE_MYSQL_HOST=localhost
147
+ STORAGE_MYSQL_PORT=3306
148
+ STORAGE_MYSQL_USER=root
149
+ STORAGE_MYSQL_PASSWORD=password
150
+ STORAGE_MYSQL_DATABASE=myapp
151
+ ```
152
+
153
+ **Example 2: Multiple MySQL Databases (Prod + Staging)**
154
+
155
+ ```bash
156
+ # Production (read-only)
157
+ STORAGE_PROD_TYPE=mysql
158
+ STORAGE_PROD_HOST=prod-db.example.com
159
+ STORAGE_PROD_PORT=3306
160
+ STORAGE_PROD_USER=readonly
161
+ STORAGE_PROD_PASSWORD=secret
162
+ STORAGE_PROD_DATABASE=production
163
+
164
+ # Staging (read-write)
165
+ STORAGE_STAGING_TYPE=mysql
166
+ STORAGE_STAGING_HOST=staging-db.example.com
167
+ STORAGE_STAGING_PORT=3306
168
+ STORAGE_STAGING_USER=admin
169
+ STORAGE_STAGING_PASSWORD=secret
170
+ STORAGE_STAGING_DATABASE=staging
171
+ STORAGE_STAGING_WRITE_MODE=true
172
+ ```
173
+
174
+ **Example 3: MySQL + Athena**
175
+
176
+ ```bash
177
+ # MySQL for operational data
178
+ STORAGE_DB_TYPE=mysql
179
+ STORAGE_DB_HOST=localhost
180
+ STORAGE_DB_PORT=3306
181
+ STORAGE_DB_USER=admin
182
+ STORAGE_DB_PASSWORD=password
183
+ STORAGE_DB_DATABASE=myapp
184
+
185
+ # Athena for analytics
186
+ STORAGE_ANALYTICS_TYPE=athena
187
+ STORAGE_ANALYTICS_REGION=us-west-2
188
+ STORAGE_ANALYTICS_DATABASE=analytics
189
+ STORAGE_ANALYTICS_S3_OUTPUT_LOCATION=s3://my-bucket/athena-results/
190
+ ```
191
+
192
+ ## Features
193
+
194
+ ### Unified Interface
195
+ Query different databases using the same MCP tools, regardless of the underlying technology.
196
+
197
+ ### Read-Only by Default
198
+ All connections are read-only by default. Explicitly enable `WRITE_MODE` for write operations.
199
+
200
+ ### Multiple Connections
201
+ Configure multiple databases of the same or different types simultaneously.
202
+
203
+ ### Extensible Architecture
204
+ Easy to add new database connectors with the `McpConnector` interface.
205
+
206
+ ## Architecture
207
+
208
+ ```
209
+ ┌─────────────────────────────────────────┐
210
+ │ MCP Server (server.ts) │
211
+ ├─────────────────────────────────────────┤
212
+ │ MCP Tools: query, execute, list, etc. │
213
+ └─────────────┬───────────────────────────┘
214
+
215
+ ┌───────┴────────┐
216
+ │ Registry │
217
+ └───────┬────────┘
218
+
219
+ ┌─────────┴──────────┐
220
+ │ │
221
+ ┌───▼──────┐ ┌─────▼─────┐
222
+ │ MySQL │ │ Athena │
223
+ │Connector │ │ Connector │
224
+ └──────────┘ └───────────┘
225
+ ```
226
+
227
+ Each connector implements the `McpConnector` interface:
228
+ - `query()` - Execute read queries
229
+ - `execute()` - Execute write operations
230
+ - `listCollections()` - List tables/collections
231
+ - `describeCollection()` - Get schema information
232
+
233
+ ## Security
234
+
235
+ - Never commit credentials or API tokens to version control
236
+ - Use environment variables for sensitive configuration
237
+ - Keep `WRITE_MODE` disabled unless absolutely necessary
238
+ - Review queries before execution, especially with write access enabled
239
+
240
+ ## Development
241
+
242
+ ```bash
243
+ # Clone the repository
244
+ git clone https://github.com/cyhoon/mcp-storage-map.git
245
+ cd mcp-storage-map
246
+
247
+ # Install dependencies
248
+ pnpm install
249
+
250
+ # Run in development mode
251
+ pnpm dev
252
+
253
+ # Build
254
+ pnpm build
255
+
256
+ # Run tests
257
+ pnpm test
258
+
259
+ # Lint
260
+ pnpm lint
261
+ ```
262
+
263
+ ## License
264
+
265
+ MIT - See [LICENSE](LICENSE)
@@ -0,0 +1,14 @@
1
+ import { StorageConfig } from '../config.js';
2
+ import { McpConnector, McpConnectorResult } from '../mcp-connectors/index.js';
3
+ export declare class MySQLConnector implements McpConnector {
4
+ private pool;
5
+ private config;
6
+ constructor(config: StorageConfig);
7
+ private connect;
8
+ query(query: string, params?: any): Promise<McpConnectorResult>;
9
+ execute(operation: string, params?: any): Promise<McpConnectorResult>;
10
+ listCollections(schema?: string): Promise<McpConnectorResult>;
11
+ describeCollection(collection: string, schema?: string): Promise<McpConnectorResult>;
12
+ disconnect(): Promise<void>;
13
+ }
14
+ //# sourceMappingURL=MySqlClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MySqlClient.d.ts","sourceRoot":"","sources":["../../src/clients/MySqlClient.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAE9E,qBAAa,cAAe,YAAW,YAAY;IACjD,OAAO,CAAC,IAAI,CAA2B;IACvC,OAAO,CAAC,MAAM,CAAgB;gBAElB,MAAM,EAAE,aAAa;IAIjC,OAAO,CAAC,OAAO;IAiBT,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAyB/D,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,kBAAkB,CAAC;IA8BrE,eAAe,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAiC7D,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IA+BpF,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;CAQlC"}
@@ -0,0 +1,141 @@
1
+ import mysql from 'mysql2/promise';
2
+ export class MySQLConnector {
3
+ pool = null;
4
+ config;
5
+ constructor(config) {
6
+ this.config = config;
7
+ }
8
+ connect() {
9
+ if (!this.config.connection) {
10
+ throw new Error('MySQL connection config is missing');
11
+ }
12
+ this.pool = mysql.createPool({
13
+ host: this.config.connection.host,
14
+ port: this.config.connection.port || 3306,
15
+ user: this.config.connection.user,
16
+ password: this.config.connection.password,
17
+ database: this.config.connection.database,
18
+ waitForConnections: true,
19
+ connectionLimit: 10,
20
+ queueLimit: 0
21
+ });
22
+ }
23
+ async query(query, params) {
24
+ try {
25
+ if (!this.pool) {
26
+ this.connect();
27
+ }
28
+ const [rows] = await this.pool.execute(query, params);
29
+ return {
30
+ type: 'text',
31
+ text: JSON.stringify({
32
+ rows,
33
+ rowCount: Array.isArray(rows) ? rows.length : 0
34
+ }, null, 2)
35
+ };
36
+ }
37
+ catch (error) {
38
+ return {
39
+ type: 'error',
40
+ text: JSON.stringify({
41
+ error: error.message || 'Unknown error occurred',
42
+ query
43
+ }, null, 2)
44
+ };
45
+ }
46
+ }
47
+ async execute(operation, params) {
48
+ try {
49
+ if (!this.config.writeMode) {
50
+ throw new Error('Write operations are not allowed for this connection');
51
+ }
52
+ if (!this.pool) {
53
+ this.connect();
54
+ }
55
+ const [result] = await this.pool.execute(operation, params);
56
+ return {
57
+ type: 'text',
58
+ text: JSON.stringify({
59
+ affectedRows: result.affectedRows || 0,
60
+ insertId: result.insertId,
61
+ message: result.message
62
+ }, null, 2)
63
+ };
64
+ }
65
+ catch (error) {
66
+ return {
67
+ type: 'error',
68
+ text: JSON.stringify({
69
+ error: error.message || 'Unknown error occurred',
70
+ operation
71
+ }, null, 2)
72
+ };
73
+ }
74
+ }
75
+ async listCollections(schema) {
76
+ try {
77
+ if (!this.pool) {
78
+ this.connect();
79
+ }
80
+ const database = schema || this.config.connection?.database;
81
+ const sqlQuery = database
82
+ ? 'SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = ?'
83
+ : 'SHOW TABLES';
84
+ const [rows] = await this.pool.execute(sqlQuery, database ? [database] : undefined);
85
+ const tables = rows.map((row) => Object.values(row)[0]);
86
+ return {
87
+ type: 'text',
88
+ text: JSON.stringify({
89
+ tables,
90
+ count: tables.length,
91
+ database: database
92
+ }, null, 2)
93
+ };
94
+ }
95
+ catch (error) {
96
+ return {
97
+ type: 'error',
98
+ text: JSON.stringify({
99
+ error: error.message || 'Unknown error occurred',
100
+ schema
101
+ }, null, 2)
102
+ };
103
+ }
104
+ }
105
+ async describeCollection(collection, schema) {
106
+ try {
107
+ if (!this.pool) {
108
+ this.connect();
109
+ }
110
+ const database = schema || this.config.connection?.database;
111
+ const fullTableName = database ? `${database}.${collection}` : collection;
112
+ const [columns] = await this.pool.execute(`DESCRIBE ${fullTableName}`);
113
+ return {
114
+ type: 'text',
115
+ text: JSON.stringify({
116
+ table: collection,
117
+ columns,
118
+ database: database
119
+ }, null, 2)
120
+ };
121
+ }
122
+ catch (error) {
123
+ return {
124
+ type: 'error',
125
+ text: JSON.stringify({
126
+ error: error.message || 'Unknown error occurred',
127
+ table: collection,
128
+ schema
129
+ }, null, 2)
130
+ };
131
+ }
132
+ }
133
+ async disconnect() {
134
+ if (!this.pool) {
135
+ return;
136
+ }
137
+ await this.pool.end();
138
+ this.pool = null;
139
+ }
140
+ }
141
+ //# sourceMappingURL=MySqlClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MySqlClient.js","sourceRoot":"","sources":["../../src/clients/MySqlClient.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import { StorageConfig } from '../../config.js';
2
+ import { McpConnector, McpConnectorResult } from '../../mcp-connectors/index.js';
3
+ export declare class AthenaConnector implements McpConnector {
4
+ private client;
5
+ private config;
6
+ private outputLocation;
7
+ private database;
8
+ private workGroup;
9
+ constructor(config: StorageConfig);
10
+ query(query: string, _params?: any): Promise<McpConnectorResult>;
11
+ execute(operation: string, params?: any): Promise<McpConnectorResult>;
12
+ listCollections(schema?: string): Promise<McpConnectorResult>;
13
+ describeCollection(collection: string, schema?: string): Promise<McpConnectorResult>;
14
+ private waitForQueryCompletion;
15
+ private formatResults;
16
+ }
17
+ //# sourceMappingURL=AthenaClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AthenaClient.d.ts","sourceRoot":"","sources":["../../../src/clients/athena-client/AthenaClient.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAEjF,qBAAa,eAAgB,YAAW,YAAY;IAClD,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,SAAS,CAAS;gBAEd,MAAM,EAAE,aAAa;IAiB3B,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAoDhE,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAoBrE,eAAe,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAgC7D,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;YA8C5E,sBAAsB;IA4BpC,OAAO,CAAC,aAAa;CAkCtB"}
@@ -0,0 +1,199 @@
1
+ import { AthenaClient, StartQueryExecutionCommand, GetQueryExecutionCommand, GetQueryResultsCommand, QueryExecutionState, ListTableMetadataCommand, GetTableMetadataCommand, } from '@aws-sdk/client-athena';
2
+ export class AthenaConnector {
3
+ client;
4
+ config;
5
+ outputLocation;
6
+ database;
7
+ workGroup;
8
+ constructor(config) {
9
+ this.config = config;
10
+ const region = config.connection?.region || process.env.AWS_REGION || 'us-east-1';
11
+ this.outputLocation = config.connection?.s3OutputLocation || process.env.ATHENA_S3_OUTPUT || '';
12
+ this.database = config.connection?.database || process.env.ATHENA_DATABASE || 'default';
13
+ this.workGroup = config.connection?.workgroup || process.env.ATHENA_WORKGROUP || 'primary';
14
+ if (!this.outputLocation) {
15
+ throw new Error('Athena S3 output location is required (s3OutputLocation or ATHENA_S3_OUTPUT)');
16
+ }
17
+ this.client = new AthenaClient({
18
+ region,
19
+ });
20
+ }
21
+ async query(query, _params) {
22
+ try {
23
+ const startCommand = new StartQueryExecutionCommand({
24
+ QueryString: query,
25
+ QueryExecutionContext: {
26
+ Database: this.database,
27
+ },
28
+ ResultConfiguration: {
29
+ OutputLocation: this.outputLocation,
30
+ },
31
+ WorkGroup: this.workGroup,
32
+ });
33
+ const { QueryExecutionId } = await this.client.send(startCommand);
34
+ if (!QueryExecutionId) {
35
+ throw new Error('Failed to start query execution');
36
+ }
37
+ await this.waitForQueryCompletion(QueryExecutionId);
38
+ const resultsCommand = new GetQueryResultsCommand({
39
+ QueryExecutionId,
40
+ });
41
+ const results = await this.client.send(resultsCommand);
42
+ const rows = this.formatResults(results.ResultSet);
43
+ return {
44
+ type: 'text',
45
+ text: JSON.stringify({
46
+ rows,
47
+ rowCount: rows.length,
48
+ queryExecutionId: QueryExecutionId,
49
+ }, null, 2)
50
+ };
51
+ }
52
+ catch (error) {
53
+ return {
54
+ type: 'error',
55
+ text: JSON.stringify({
56
+ error: error.message || 'Unknown error occurred',
57
+ query,
58
+ database: this.database,
59
+ }, null, 2)
60
+ };
61
+ }
62
+ }
63
+ async execute(operation, params) {
64
+ try {
65
+ if (!this.config.writeMode) {
66
+ throw new Error('Write operations are not allowed for this connection');
67
+ }
68
+ return await this.query(operation, params);
69
+ }
70
+ catch (error) {
71
+ return {
72
+ type: 'error',
73
+ text: JSON.stringify({
74
+ error: error.message || 'Unknown error occurred',
75
+ operation,
76
+ }, null, 2)
77
+ };
78
+ }
79
+ }
80
+ async listCollections(schema) {
81
+ try {
82
+ const targetDatabase = schema || this.database;
83
+ const command = new ListTableMetadataCommand({
84
+ CatalogName: 'AwsDataCatalog',
85
+ DatabaseName: targetDatabase,
86
+ });
87
+ const response = await this.client.send(command);
88
+ const tables = response.TableMetadataList?.map(table => table.Name) || [];
89
+ return {
90
+ type: 'text',
91
+ text: JSON.stringify({
92
+ tables,
93
+ count: tables.length,
94
+ database: targetDatabase,
95
+ }, null, 2)
96
+ };
97
+ }
98
+ catch (error) {
99
+ return {
100
+ type: 'error',
101
+ text: JSON.stringify({
102
+ error: error.message || 'Unknown error occurred',
103
+ database: schema || this.database,
104
+ }, null, 2)
105
+ };
106
+ }
107
+ }
108
+ async describeCollection(collection, schema) {
109
+ try {
110
+ const targetDatabase = schema || this.database;
111
+ const command = new GetTableMetadataCommand({
112
+ CatalogName: 'AwsDataCatalog',
113
+ DatabaseName: targetDatabase,
114
+ TableName: collection,
115
+ });
116
+ const response = await this.client.send(command);
117
+ const columns = response.TableMetadata?.Columns?.map(col => ({
118
+ name: col.Name,
119
+ type: col.Type,
120
+ comment: col.Comment,
121
+ })) || [];
122
+ const partitionKeys = response.TableMetadata?.PartitionKeys?.map(key => ({
123
+ name: key.Name,
124
+ type: key.Type,
125
+ })) || [];
126
+ return {
127
+ type: 'text',
128
+ text: JSON.stringify({
129
+ table: collection,
130
+ database: targetDatabase,
131
+ columns,
132
+ partitionKeys,
133
+ tableType: response.TableMetadata?.TableType,
134
+ location: response.TableMetadata?.Parameters?.['location'],
135
+ }, null, 2)
136
+ };
137
+ }
138
+ catch (error) {
139
+ return {
140
+ type: 'error',
141
+ text: JSON.stringify({
142
+ error: error.message || 'Unknown error occurred',
143
+ table: collection,
144
+ database: schema || this.database,
145
+ }, null, 2)
146
+ };
147
+ }
148
+ }
149
+ async waitForQueryCompletion(queryExecutionId) {
150
+ const maxAttempts = 100;
151
+ const delayMs = 1000;
152
+ for (let i = 0; i < maxAttempts; i++) {
153
+ const command = new GetQueryExecutionCommand({
154
+ QueryExecutionId: queryExecutionId,
155
+ });
156
+ const response = await this.client.send(command);
157
+ const state = response.QueryExecution?.Status?.State;
158
+ switch (state) {
159
+ case QueryExecutionState.SUCCEEDED:
160
+ return;
161
+ case QueryExecutionState.FAILED:
162
+ throw new Error(`Query failed: ${response.QueryExecution?.Status?.StateChangeReason}`);
163
+ case QueryExecutionState.CANCELLED:
164
+ throw new Error('Query was cancelled');
165
+ default:
166
+ await new Promise(resolve => setTimeout(resolve, delayMs));
167
+ }
168
+ }
169
+ throw new Error('Query timeout - exceeded maximum wait time');
170
+ }
171
+ formatResults(resultSet) {
172
+ if (!resultSet?.Rows || resultSet.Rows.length === 0) {
173
+ return [];
174
+ }
175
+ const firstRow = resultSet.Rows[0];
176
+ const headers = [];
177
+ if (firstRow?.Data && Array.isArray(firstRow.Data)) {
178
+ firstRow.Data.forEach((col) => {
179
+ headers.push(col.VarCharValue);
180
+ });
181
+ }
182
+ const rows = [];
183
+ for (let i = 1; i < resultSet.Rows.length; i++) {
184
+ const row = resultSet.Rows[i];
185
+ const obj = {};
186
+ if (row.Data && Array.isArray(row.Data)) {
187
+ row.Data.forEach((cell, index) => {
188
+ const header = headers[index];
189
+ if (header) {
190
+ obj[header] = cell.VarCharValue || null;
191
+ }
192
+ });
193
+ }
194
+ rows.push(obj);
195
+ }
196
+ return rows;
197
+ }
198
+ }
199
+ //# sourceMappingURL=AthenaClient.js.map