@sigma4life/mysql-mcp-server 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/.env.example +35 -0
- package/.github/workflows/ci.yml +18 -0
- package/AUTHENTICATION.md +24 -0
- package/CLAUDE.md +37 -0
- package/CONTRIBUTING.md +19 -0
- package/LICENSE +21 -0
- package/QUERY_ACCESS_SETUP.md +65 -0
- package/README.md +144 -0
- package/SECURITY.md +10 -0
- package/TESTING.md +54 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +197 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/cache.d.ts +11 -0
- package/dist/core/cache.d.ts.map +1 -0
- package/dist/core/cache.js +30 -0
- package/dist/core/cache.js.map +1 -0
- package/dist/core/config.d.ts +24 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +63 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/database-url.d.ts +11 -0
- package/dist/core/database-url.d.ts.map +1 -0
- package/dist/core/database-url.js +37 -0
- package/dist/core/database-url.js.map +1 -0
- package/dist/core/database-url.test.d.ts +2 -0
- package/dist/core/database-url.test.d.ts.map +1 -0
- package/dist/core/database-url.test.js +22 -0
- package/dist/core/database-url.test.js.map +1 -0
- package/dist/core/logger.d.ts +3 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +17 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/mcp.d.ts +14 -0
- package/dist/core/mcp.d.ts.map +1 -0
- package/dist/core/mcp.js +23 -0
- package/dist/core/mcp.js.map +1 -0
- package/dist/core/query-safety.d.ts +10 -0
- package/dist/core/query-safety.d.ts.map +1 -0
- package/dist/core/query-safety.js +50 -0
- package/dist/core/query-safety.js.map +1 -0
- package/dist/core/query-safety.test.d.ts +2 -0
- package/dist/core/query-safety.test.d.ts.map +1 -0
- package/dist/core/query-safety.test.js +30 -0
- package/dist/core/query-safety.test.js.map +1 -0
- package/dist/core/schema-types.d.ts +53 -0
- package/dist/core/schema-types.d.ts.map +1 -0
- package/dist/core/schema-types.js +2 -0
- package/dist/core/schema-types.js.map +1 -0
- package/dist/core/security/access-control.d.ts +32 -0
- package/dist/core/security/access-control.d.ts.map +1 -0
- package/dist/core/security/access-control.js +244 -0
- package/dist/core/security/access-control.js.map +1 -0
- package/dist/core/security/config-loader.d.ts +20 -0
- package/dist/core/security/config-loader.d.ts.map +1 -0
- package/dist/core/security/config-loader.js +227 -0
- package/dist/core/security/config-loader.js.map +1 -0
- package/dist/core/security/types.d.ts +64 -0
- package/dist/core/security/types.d.ts.map +1 -0
- package/dist/core/security/types.js +28 -0
- package/dist/core/security/types.js.map +1 -0
- package/dist/core/sql-parser.d.ts +23 -0
- package/dist/core/sql-parser.d.ts.map +1 -0
- package/dist/core/sql-parser.js +460 -0
- package/dist/core/sql-parser.js.map +1 -0
- package/dist/core/sql-parser.test.d.ts +2 -0
- package/dist/core/sql-parser.test.d.ts.map +1 -0
- package/dist/core/sql-parser.test.js +21 -0
- package/dist/core/sql-parser.test.js.map +1 -0
- package/dist/handlers/accessible-schema.d.ts +53 -0
- package/dist/handlers/accessible-schema.d.ts.map +1 -0
- package/dist/handlers/accessible-schema.js +192 -0
- package/dist/handlers/accessible-schema.js.map +1 -0
- package/dist/handlers/data.d.ts +17 -0
- package/dist/handlers/data.d.ts.map +1 -0
- package/dist/handlers/data.js +30 -0
- package/dist/handlers/data.js.map +1 -0
- package/dist/handlers/relationships.d.ts +14 -0
- package/dist/handlers/relationships.d.ts.map +1 -0
- package/dist/handlers/relationships.js +77 -0
- package/dist/handlers/relationships.js.map +1 -0
- package/dist/handlers/schema.d.ts +14 -0
- package/dist/handlers/schema.d.ts.map +1 -0
- package/dist/handlers/schema.js +104 -0
- package/dist/handlers/schema.js.map +1 -0
- package/dist/handlers/search.d.ts +14 -0
- package/dist/handlers/search.d.ts.map +1 -0
- package/dist/handlers/search.js +18 -0
- package/dist/handlers/search.js.map +1 -0
- package/dist/handlers/validation.d.ts +32 -0
- package/dist/handlers/validation.d.ts.map +1 -0
- package/dist/handlers/validation.js +116 -0
- package/dist/handlers/validation.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +294 -0
- package/dist/index.js.map +1 -0
- package/dist/mysql/connection.d.ts +15 -0
- package/dist/mysql/connection.d.ts.map +1 -0
- package/dist/mysql/connection.js +57 -0
- package/dist/mysql/connection.js.map +1 -0
- package/dist/mysql/identifiers.d.ts +3 -0
- package/dist/mysql/identifiers.d.ts.map +1 -0
- package/dist/mysql/identifiers.js +10 -0
- package/dist/mysql/identifiers.js.map +1 -0
- package/dist/mysql/identifiers.test.d.ts +2 -0
- package/dist/mysql/identifiers.test.d.ts.map +1 -0
- package/dist/mysql/identifiers.test.js +11 -0
- package/dist/mysql/identifiers.test.js.map +1 -0
- package/dist/mysql/queries.d.ts +49 -0
- package/dist/mysql/queries.d.ts.map +1 -0
- package/dist/mysql/queries.js +206 -0
- package/dist/mysql/queries.js.map +1 -0
- package/docs/clients/claude-code.md +28 -0
- package/docs/clients/claude-desktop.md +35 -0
- package/docs/clients/codex.md +28 -0
- package/docs/clients/cursor.md +26 -0
- package/docs/clients/opencode.md +35 -0
- package/docs/clients/vscode.md +25 -0
- package/package.json +49 -0
- package/query-access.example.json +21 -0
- package/src/cli.ts +221 -0
- package/src/core/cache.ts +41 -0
- package/src/core/config.ts +97 -0
- package/src/core/database-url.test.ts +28 -0
- package/src/core/database-url.ts +47 -0
- package/src/core/logger.ts +24 -0
- package/src/core/mcp.ts +23 -0
- package/src/core/query-safety.test.ts +36 -0
- package/src/core/query-safety.ts +63 -0
- package/src/core/schema-types.ts +58 -0
- package/src/core/security/access-control.ts +321 -0
- package/src/core/security/config-loader.ts +315 -0
- package/src/core/security/types.ts +114 -0
- package/src/core/sql-parser.test.ts +37 -0
- package/src/core/sql-parser.ts +572 -0
- package/src/handlers/accessible-schema.ts +314 -0
- package/src/handlers/data.ts +66 -0
- package/src/handlers/relationships.ts +114 -0
- package/src/handlers/schema.ts +154 -0
- package/src/handlers/search.ts +34 -0
- package/src/handlers/validation.ts +165 -0
- package/src/index.ts +337 -0
- package/src/mysql/connection.ts +68 -0
- package/src/mysql/identifiers.test.ts +12 -0
- package/src/mysql/identifiers.ts +10 -0
- package/src/mysql/queries.ts +285 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { resolveDatabase, resolveSchema } from '../core/config.js';
|
|
2
|
+
import { logger } from '../core/logger.js';
|
|
3
|
+
import { db } from '../mysql/connection.js';
|
|
4
|
+
export async function validateDatabase(database) {
|
|
5
|
+
const actualName = resolveDatabase(database);
|
|
6
|
+
return {
|
|
7
|
+
exists: true,
|
|
8
|
+
actualName,
|
|
9
|
+
message: database && database !== actualName
|
|
10
|
+
? `Database found (case mismatch): '${actualName}' (you provided '${database}')`
|
|
11
|
+
: `Database '${actualName}' is configured for this server`,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export async function validateSchema(database, schema) {
|
|
15
|
+
const actualDatabase = resolveDatabase(database);
|
|
16
|
+
const actualSchema = resolveSchema(schema);
|
|
17
|
+
return {
|
|
18
|
+
exists: true,
|
|
19
|
+
actualName: actualSchema,
|
|
20
|
+
message: `Schema '${actualSchema}' is available in database '${actualDatabase}'`,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export async function validateTable(database, table, schema) {
|
|
24
|
+
const actualDatabase = resolveDatabase(database);
|
|
25
|
+
resolveSchema(schema);
|
|
26
|
+
try {
|
|
27
|
+
const matches = await db.query(`
|
|
28
|
+
SELECT
|
|
29
|
+
TABLE_SCHEMA AS schemaName,
|
|
30
|
+
TABLE_NAME AS tableName,
|
|
31
|
+
CONCAT(TABLE_SCHEMA, '.', TABLE_NAME) AS fullName,
|
|
32
|
+
TABLE_TYPE AS objectType,
|
|
33
|
+
TABLE_ROWS AS rowCount
|
|
34
|
+
FROM information_schema.TABLES
|
|
35
|
+
WHERE TABLE_SCHEMA = :database
|
|
36
|
+
AND LOWER(TABLE_NAME) = LOWER(:table)
|
|
37
|
+
AND TABLE_TYPE IN ('BASE TABLE', 'VIEW')
|
|
38
|
+
ORDER BY TABLE_NAME
|
|
39
|
+
`, { database: actualDatabase, table });
|
|
40
|
+
if (matches.length === 1) {
|
|
41
|
+
const match = matches[0];
|
|
42
|
+
return {
|
|
43
|
+
exists: true,
|
|
44
|
+
actualName: match.fullName,
|
|
45
|
+
message: match.tableName === table
|
|
46
|
+
? `Table '${match.fullName}' exists`
|
|
47
|
+
: `Table found (case mismatch): '${match.fullName}' (you provided '${table}')`,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const suggestions = await db.query(`
|
|
51
|
+
SELECT
|
|
52
|
+
TABLE_SCHEMA AS schemaName,
|
|
53
|
+
TABLE_NAME AS tableName,
|
|
54
|
+
CONCAT(TABLE_SCHEMA, '.', TABLE_NAME) AS fullName,
|
|
55
|
+
TABLE_TYPE AS objectType,
|
|
56
|
+
TABLE_ROWS AS rowCount
|
|
57
|
+
FROM information_schema.TABLES
|
|
58
|
+
WHERE TABLE_SCHEMA = :database
|
|
59
|
+
AND LOWER(TABLE_NAME) LIKE CONCAT('%', LOWER(:table), '%')
|
|
60
|
+
AND TABLE_TYPE IN ('BASE TABLE', 'VIEW')
|
|
61
|
+
ORDER BY TABLE_NAME
|
|
62
|
+
LIMIT 10
|
|
63
|
+
`, { database: actualDatabase, table });
|
|
64
|
+
const tables = suggestions.map((row) => ({
|
|
65
|
+
schema: row.schemaName,
|
|
66
|
+
table: row.tableName,
|
|
67
|
+
fullName: row.fullName,
|
|
68
|
+
type: row.objectType,
|
|
69
|
+
rowCount: row.rowCount,
|
|
70
|
+
}));
|
|
71
|
+
return {
|
|
72
|
+
exists: false,
|
|
73
|
+
tables,
|
|
74
|
+
suggestions: tables.slice(0, 5).map((row) => row.fullName),
|
|
75
|
+
message: tables.length
|
|
76
|
+
? `Table '${table}' not found in database '${actualDatabase}'. Did you mean: ${tables.slice(0, 5).map((row) => row.fullName).join(', ')}?`
|
|
77
|
+
: `Table '${table}' not found in database '${actualDatabase}'. No similar tables found.`,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
logger.error('Table validation failed:', error);
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
export async function validateDatabaseObject(database, table, schema) {
|
|
86
|
+
const databaseValidation = await validateDatabase(database);
|
|
87
|
+
const schemaValidation = schema
|
|
88
|
+
? await validateSchema(databaseValidation.actualName, schema)
|
|
89
|
+
: undefined;
|
|
90
|
+
if (table) {
|
|
91
|
+
const tableValidation = await validateTable(databaseValidation.actualName, table, schemaValidation?.actualName);
|
|
92
|
+
if (!tableValidation.exists) {
|
|
93
|
+
return {
|
|
94
|
+
valid: false,
|
|
95
|
+
database: databaseValidation,
|
|
96
|
+
schema: schemaValidation,
|
|
97
|
+
table: tableValidation,
|
|
98
|
+
message: tableValidation.message,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
valid: true,
|
|
103
|
+
database: databaseValidation,
|
|
104
|
+
schema: schemaValidation,
|
|
105
|
+
table: tableValidation,
|
|
106
|
+
message: `Validation successful: ${tableValidation.actualName}`,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
valid: true,
|
|
111
|
+
database: databaseValidation,
|
|
112
|
+
schema: schemaValidation,
|
|
113
|
+
message: 'Validation successful',
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/handlers/validation.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
5
|
+
import { appConfig } from './core/config.js';
|
|
6
|
+
import { errorResult, textResult } from './core/mcp.js';
|
|
7
|
+
import { logger } from './core/logger.js';
|
|
8
|
+
import { initAccessControl, loadAccessControlConfig, } from './core/security/access-control.js';
|
|
9
|
+
import { executeQuery } from './handlers/data.js';
|
|
10
|
+
import { getAccessibleSchema, getAccessibleTableInfo, } from './handlers/accessible-schema.js';
|
|
11
|
+
import { getRelationships } from './handlers/relationships.js';
|
|
12
|
+
import { getSchema, getTableInfo } from './handlers/schema.js';
|
|
13
|
+
import { findTables, searchObjects } from './handlers/search.js';
|
|
14
|
+
import { validateDatabaseObject } from './handlers/validation.js';
|
|
15
|
+
import { db } from './mysql/connection.js';
|
|
16
|
+
const databaseProperty = {
|
|
17
|
+
type: 'string',
|
|
18
|
+
description: 'Optional compatibility field. If provided, it must match the configured DB_NAME.',
|
|
19
|
+
};
|
|
20
|
+
const schemaProperty = {
|
|
21
|
+
type: 'string',
|
|
22
|
+
description: 'Optional compatibility field. MySQL uses DB_NAME as the schema for this server.',
|
|
23
|
+
};
|
|
24
|
+
const tools = [
|
|
25
|
+
{
|
|
26
|
+
name: 'get_schema',
|
|
27
|
+
description: 'Retrieves MySQL schema information for one or more tables. Returns columns, data types, primary keys, foreign keys, indexes, and optional table statistics.',
|
|
28
|
+
inputSchema: {
|
|
29
|
+
type: 'object',
|
|
30
|
+
properties: {
|
|
31
|
+
database: databaseProperty,
|
|
32
|
+
tables: {
|
|
33
|
+
type: 'array',
|
|
34
|
+
items: { type: 'string' },
|
|
35
|
+
description: 'Table names to retrieve. Leave empty to get all tables in the configured database.',
|
|
36
|
+
},
|
|
37
|
+
schema: schemaProperty,
|
|
38
|
+
includeRelationships: {
|
|
39
|
+
type: 'boolean',
|
|
40
|
+
description: 'Include foreign key relationships (default: true)',
|
|
41
|
+
default: true,
|
|
42
|
+
},
|
|
43
|
+
includeStatistics: {
|
|
44
|
+
type: 'boolean',
|
|
45
|
+
description: 'Include approximate table row/size statistics (default: false)',
|
|
46
|
+
default: false,
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
required: [],
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: 'get_table_info',
|
|
54
|
+
description: 'Gets detailed metadata for a single MySQL table or view. Use get_schema for batch table metadata.',
|
|
55
|
+
inputSchema: {
|
|
56
|
+
type: 'object',
|
|
57
|
+
properties: {
|
|
58
|
+
database: databaseProperty,
|
|
59
|
+
table: {
|
|
60
|
+
type: 'string',
|
|
61
|
+
description: 'Table or view name, for example "customers"',
|
|
62
|
+
},
|
|
63
|
+
schema: schemaProperty,
|
|
64
|
+
},
|
|
65
|
+
required: ['table'],
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: 'find_tables',
|
|
70
|
+
description: 'Searches MySQL tables/views by name pattern or by containing a matching column. Supports * and ? wildcards.',
|
|
71
|
+
inputSchema: {
|
|
72
|
+
type: 'object',
|
|
73
|
+
properties: {
|
|
74
|
+
database: databaseProperty,
|
|
75
|
+
pattern: {
|
|
76
|
+
type: 'string',
|
|
77
|
+
description: 'Table name pattern, for example "*customer*" or "order_*".',
|
|
78
|
+
},
|
|
79
|
+
hasColumn: {
|
|
80
|
+
type: 'string',
|
|
81
|
+
description: 'Column name pattern, for example "*email*" or "created_at".',
|
|
82
|
+
},
|
|
83
|
+
schema: schemaProperty,
|
|
84
|
+
},
|
|
85
|
+
required: [],
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: 'search_objects',
|
|
90
|
+
description: 'Searches MySQL table and column names. Returns matching table and column references.',
|
|
91
|
+
inputSchema: {
|
|
92
|
+
type: 'object',
|
|
93
|
+
properties: {
|
|
94
|
+
database: databaseProperty,
|
|
95
|
+
search: {
|
|
96
|
+
type: 'string',
|
|
97
|
+
description: 'Search string or wildcard pattern, for example "order" or "*email*".',
|
|
98
|
+
},
|
|
99
|
+
schema: schemaProperty,
|
|
100
|
+
type: {
|
|
101
|
+
type: 'string',
|
|
102
|
+
enum: ['table', 'column'],
|
|
103
|
+
description: 'Optional object type filter.',
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
required: ['search'],
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
name: 'get_relationships',
|
|
111
|
+
description: 'Maps foreign key relationships for JOIN path discovery between MySQL tables.',
|
|
112
|
+
inputSchema: {
|
|
113
|
+
type: 'object',
|
|
114
|
+
properties: {
|
|
115
|
+
database: databaseProperty,
|
|
116
|
+
fromTable: {
|
|
117
|
+
type: 'string',
|
|
118
|
+
description: 'Source table name',
|
|
119
|
+
},
|
|
120
|
+
toTable: {
|
|
121
|
+
type: 'string',
|
|
122
|
+
description: 'Target table name. If omitted, returns direct relationships for fromTable.',
|
|
123
|
+
},
|
|
124
|
+
maxDepth: {
|
|
125
|
+
type: 'number',
|
|
126
|
+
description: 'Maximum relationship traversal depth (default: 2)',
|
|
127
|
+
default: 2,
|
|
128
|
+
},
|
|
129
|
+
schema: schemaProperty,
|
|
130
|
+
},
|
|
131
|
+
required: ['fromTable'],
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
name: 'validate_objects',
|
|
136
|
+
description: 'Validates the configured database and optional MySQL table names, with suggestions for close table-name matches.',
|
|
137
|
+
inputSchema: {
|
|
138
|
+
type: 'object',
|
|
139
|
+
properties: {
|
|
140
|
+
database: databaseProperty,
|
|
141
|
+
table: {
|
|
142
|
+
type: 'string',
|
|
143
|
+
description: 'Single table name to validate.',
|
|
144
|
+
},
|
|
145
|
+
tables: {
|
|
146
|
+
type: 'array',
|
|
147
|
+
items: { type: 'string' },
|
|
148
|
+
description: 'Multiple table names to validate.',
|
|
149
|
+
},
|
|
150
|
+
schema: schemaProperty,
|
|
151
|
+
},
|
|
152
|
+
required: [],
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
name: 'get_accessible_schema',
|
|
157
|
+
description: 'Shows tables and columns accessible for SELECT queries based on QUERY_ACCESS_CONFIG.',
|
|
158
|
+
inputSchema: {
|
|
159
|
+
type: 'object',
|
|
160
|
+
properties: {
|
|
161
|
+
database: databaseProperty,
|
|
162
|
+
schema: schemaProperty,
|
|
163
|
+
},
|
|
164
|
+
required: [],
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
name: 'get_accessible_table_info',
|
|
169
|
+
description: 'Shows table columns with access status according to QUERY_ACCESS_CONFIG.',
|
|
170
|
+
inputSchema: {
|
|
171
|
+
type: 'object',
|
|
172
|
+
properties: {
|
|
173
|
+
database: databaseProperty,
|
|
174
|
+
table: {
|
|
175
|
+
type: 'string',
|
|
176
|
+
description: 'Table name to check.',
|
|
177
|
+
},
|
|
178
|
+
schema: schemaProperty,
|
|
179
|
+
},
|
|
180
|
+
required: ['table'],
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
...(!appConfig.server.schemaOnlyMode
|
|
184
|
+
? [
|
|
185
|
+
{
|
|
186
|
+
name: 'execute_query',
|
|
187
|
+
description: 'Executes a guarded read-only MySQL SELECT query. Queries are validated, access-controlled, and automatically limited with LIMIT.',
|
|
188
|
+
inputSchema: {
|
|
189
|
+
type: 'object',
|
|
190
|
+
properties: {
|
|
191
|
+
database: databaseProperty,
|
|
192
|
+
query: {
|
|
193
|
+
type: 'string',
|
|
194
|
+
description: 'Read-only SELECT query. Supports joins, CTEs, subqueries, WHERE, GROUP BY, HAVING, ORDER BY, and LIMIT.',
|
|
195
|
+
},
|
|
196
|
+
parameters: {
|
|
197
|
+
type: 'object',
|
|
198
|
+
description: 'Optional named parameters for mysql2 named placeholders, for example {"id": 123}.',
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
required: ['query'],
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
]
|
|
205
|
+
: []),
|
|
206
|
+
];
|
|
207
|
+
const server = new Server({
|
|
208
|
+
name: appConfig.server.name,
|
|
209
|
+
version: appConfig.server.version,
|
|
210
|
+
}, {
|
|
211
|
+
capabilities: {
|
|
212
|
+
tools: {},
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools }));
|
|
216
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
217
|
+
const { name, arguments: args = {} } = request.params;
|
|
218
|
+
try {
|
|
219
|
+
logger.info(`Tool called: ${name}`, args);
|
|
220
|
+
switch (name) {
|
|
221
|
+
case 'get_schema':
|
|
222
|
+
return textResult(await getSchema(args));
|
|
223
|
+
case 'get_table_info':
|
|
224
|
+
return textResult(await getTableInfo(args));
|
|
225
|
+
case 'find_tables':
|
|
226
|
+
return textResult(await findTables(args));
|
|
227
|
+
case 'search_objects':
|
|
228
|
+
return textResult(await searchObjects(args));
|
|
229
|
+
case 'get_relationships':
|
|
230
|
+
return textResult(await getRelationships(args));
|
|
231
|
+
case 'validate_objects': {
|
|
232
|
+
const { database, table, tables, schema } = args;
|
|
233
|
+
if (Array.isArray(tables)) {
|
|
234
|
+
return textResult(await Promise.all(tables.map((candidate) => validateDatabaseObject(database, candidate, schema))));
|
|
235
|
+
}
|
|
236
|
+
return textResult(await validateDatabaseObject(database, table, schema));
|
|
237
|
+
}
|
|
238
|
+
case 'get_accessible_schema':
|
|
239
|
+
return textResult(await getAccessibleSchema(args));
|
|
240
|
+
case 'get_accessible_table_info':
|
|
241
|
+
return textResult(await getAccessibleTableInfo(args));
|
|
242
|
+
case 'execute_query':
|
|
243
|
+
if (appConfig.server.schemaOnlyMode) {
|
|
244
|
+
throw new Error('Data query operations are disabled because SCHEMA_ONLY_MODE=true.');
|
|
245
|
+
}
|
|
246
|
+
return textResult(await executeQuery(args));
|
|
247
|
+
default:
|
|
248
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
catch (error) {
|
|
252
|
+
logger.error(`Error executing tool ${name}:`, error);
|
|
253
|
+
return errorResult(error);
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
async function main() {
|
|
257
|
+
try {
|
|
258
|
+
await db.connect();
|
|
259
|
+
if (appConfig.server.schemaOnlyMode) {
|
|
260
|
+
logger.info('SCHEMA_ONLY_MODE enabled - execute_query is disabled');
|
|
261
|
+
}
|
|
262
|
+
else if (process.env.QUERY_ACCESS_CONFIG) {
|
|
263
|
+
try {
|
|
264
|
+
initAccessControl(loadAccessControlConfig());
|
|
265
|
+
logger.info('Access control enabled for execute_query');
|
|
266
|
+
}
|
|
267
|
+
catch (error) {
|
|
268
|
+
logger.error('Failed to load access control config:', error);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
logger.warn('QUERY_ACCESS_CONFIG not set - execute_query will be blocked');
|
|
273
|
+
}
|
|
274
|
+
const transport = new StdioServerTransport();
|
|
275
|
+
await server.connect(transport);
|
|
276
|
+
logger.info(`${appConfig.server.name} v${appConfig.server.version} started`);
|
|
277
|
+
}
|
|
278
|
+
catch (error) {
|
|
279
|
+
logger.error('Failed to start server:', error);
|
|
280
|
+
process.exit(1);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
process.on('SIGINT', async () => {
|
|
284
|
+
logger.info('Shutting down...');
|
|
285
|
+
await db.close();
|
|
286
|
+
process.exit(0);
|
|
287
|
+
});
|
|
288
|
+
process.on('SIGTERM', async () => {
|
|
289
|
+
logger.info('Shutting down...');
|
|
290
|
+
await db.close();
|
|
291
|
+
process.exit(0);
|
|
292
|
+
});
|
|
293
|
+
main();
|
|
294
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Pool, RowDataPacket } from 'mysql2/promise';
|
|
2
|
+
declare class MySqlConnection {
|
|
3
|
+
private static instance;
|
|
4
|
+
private pool;
|
|
5
|
+
private constructor();
|
|
6
|
+
static getInstance(): MySqlConnection;
|
|
7
|
+
private createPool;
|
|
8
|
+
connect(): Promise<Pool>;
|
|
9
|
+
query<T extends RowDataPacket = RowDataPacket>(sql: string, params?: Record<string, unknown>): Promise<T[]>;
|
|
10
|
+
close(): Promise<void>;
|
|
11
|
+
isConnected(): boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare const db: MySqlConnection;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=connection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/mysql/connection.ts"],"names":[],"mappings":"AAAA,OAAc,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAI5D,cAAM,eAAe;IACnB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAkB;IACzC,OAAO,CAAC,IAAI,CAAqB;IAEjC,OAAO;IAEP,MAAM,CAAC,WAAW,IAAI,eAAe;IAOrC,OAAO,CAAC,UAAU;IAkBZ,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IASxB,KAAK,CAAC,CAAC,SAAS,aAAa,GAAG,aAAa,EACjD,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,OAAO,CAAC,CAAC,EAAE,CAAC;IAOT,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ5B,WAAW,IAAI,OAAO;CAGvB;AAED,eAAO,MAAM,EAAE,iBAAgC,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import mysql from 'mysql2/promise';
|
|
2
|
+
import { appConfig } from '../core/config.js';
|
|
3
|
+
import { logger } from '../core/logger.js';
|
|
4
|
+
class MySqlConnection {
|
|
5
|
+
static instance;
|
|
6
|
+
pool = null;
|
|
7
|
+
constructor() { }
|
|
8
|
+
static getInstance() {
|
|
9
|
+
if (!MySqlConnection.instance) {
|
|
10
|
+
MySqlConnection.instance = new MySqlConnection();
|
|
11
|
+
}
|
|
12
|
+
return MySqlConnection.instance;
|
|
13
|
+
}
|
|
14
|
+
createPool() {
|
|
15
|
+
logger.info(`Creating MySQL pool for ${appConfig.db.host}:${appConfig.db.port}/${appConfig.db.name}`);
|
|
16
|
+
return mysql.createPool({
|
|
17
|
+
host: appConfig.db.host,
|
|
18
|
+
port: appConfig.db.port,
|
|
19
|
+
database: appConfig.db.name,
|
|
20
|
+
user: appConfig.db.user,
|
|
21
|
+
password: appConfig.db.password,
|
|
22
|
+
ssl: appConfig.db.ssl ? { rejectUnauthorized: true } : undefined,
|
|
23
|
+
namedPlaceholders: true,
|
|
24
|
+
waitForConnections: true,
|
|
25
|
+
connectionLimit: 10,
|
|
26
|
+
maxIdle: 10,
|
|
27
|
+
idleTimeout: 30000,
|
|
28
|
+
queueLimit: 0,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
async connect() {
|
|
32
|
+
if (!this.pool) {
|
|
33
|
+
this.pool = this.createPool();
|
|
34
|
+
await this.pool.query('SELECT 1');
|
|
35
|
+
logger.info('Connected to MySQL successfully');
|
|
36
|
+
}
|
|
37
|
+
return this.pool;
|
|
38
|
+
}
|
|
39
|
+
async query(sql, params) {
|
|
40
|
+
const pool = await this.connect();
|
|
41
|
+
logger.debug(`Executing query: ${sql}`);
|
|
42
|
+
const [rows] = await pool.query(sql, (params || {}));
|
|
43
|
+
return rows;
|
|
44
|
+
}
|
|
45
|
+
async close() {
|
|
46
|
+
if (this.pool) {
|
|
47
|
+
await this.pool.end();
|
|
48
|
+
this.pool = null;
|
|
49
|
+
logger.info('MySQL connection pool closed');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
isConnected() {
|
|
53
|
+
return this.pool !== null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export const db = MySqlConnection.getInstance();
|
|
57
|
+
//# sourceMappingURL=connection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/mysql/connection.ts"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAE3C,MAAM,eAAe;IACX,MAAM,CAAC,QAAQ,CAAkB;IACjC,IAAI,GAAgB,IAAI,CAAC;IAEjC,gBAAuB,CAAC;IAExB,MAAM,CAAC,WAAW;QAChB,IAAI,CAAC,eAAe,CAAC,QAAQ,EAAE,CAAC;YAC9B,eAAe,CAAC,QAAQ,GAAG,IAAI,eAAe,EAAE,CAAC;QACnD,CAAC;QACD,OAAO,eAAe,CAAC,QAAQ,CAAC;IAClC,CAAC;IAEO,UAAU;QAChB,MAAM,CAAC,IAAI,CAAC,2BAA2B,SAAS,CAAC,EAAE,CAAC,IAAI,IAAI,SAAS,CAAC,EAAE,CAAC,IAAI,IAAI,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;QACtG,OAAO,KAAK,CAAC,UAAU,CAAC;YACtB,IAAI,EAAE,SAAS,CAAC,EAAE,CAAC,IAAI;YACvB,IAAI,EAAE,SAAS,CAAC,EAAE,CAAC,IAAI;YACvB,QAAQ,EAAE,SAAS,CAAC,EAAE,CAAC,IAAI;YAC3B,IAAI,EAAE,SAAS,CAAC,EAAE,CAAC,IAAI;YACvB,QAAQ,EAAE,SAAS,CAAC,EAAE,CAAC,QAAQ;YAC/B,GAAG,EAAE,SAAS,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS;YAChE,iBAAiB,EAAE,IAAI;YACvB,kBAAkB,EAAE,IAAI;YACxB,eAAe,EAAE,EAAE;YACnB,OAAO,EAAE,EAAE;YACX,WAAW,EAAE,KAAK;YAClB,UAAU,EAAE,CAAC;SACd,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,OAAO;QACX,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YACf,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;YAC9B,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;YAClC,MAAM,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC;QACjD,CAAC;QACD,OAAO,IAAI,CAAC,IAAI,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,KAAK,CACT,GAAW,EACX,MAAgC;QAEhC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QAClC,MAAM,CAAC,KAAK,CAAC,oBAAoB,GAAG,EAAE,CAAC,CAAC;QACxC,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC,KAAK,CAAM,GAAG,EAAE,CAAC,MAAM,IAAI,EAAE,CAAQ,CAAC,CAAC;QACjE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;YACtB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;YACjB,MAAM,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC;IAC5B,CAAC;CACF;AAED,MAAM,CAAC,MAAM,EAAE,GAAG,eAAe,CAAC,WAAW,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identifiers.d.ts","sourceRoot":"","sources":["../../src/mysql/identifiers.ts"],"names":[],"mappings":"AAAA,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAK3D"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function quoteIdentifier(identifier) {
|
|
2
|
+
return `\`${identifier.replace(/`/g, '``')}\``;
|
|
3
|
+
}
|
|
4
|
+
export function likePattern(pattern) {
|
|
5
|
+
if (!pattern) {
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
return pattern.replace(/\*/g, '%').replace(/\?/g, '_');
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=identifiers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identifiers.js","sourceRoot":"","sources":["../../src/mysql/identifiers.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,eAAe,CAAC,UAAkB;IAChD,OAAO,KAAK,UAAU,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,OAAgB;IAC1C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AACzD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identifiers.test.d.ts","sourceRoot":"","sources":["../../src/mysql/identifiers.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import { likePattern, quoteIdentifier } from './identifiers.js';
|
|
4
|
+
test('quoteIdentifier escapes backticks', () => {
|
|
5
|
+
assert.equal(quoteIdentifier('order`items'), '`order``items`');
|
|
6
|
+
});
|
|
7
|
+
test('likePattern converts simple wildcards', () => {
|
|
8
|
+
assert.equal(likePattern('*customer?'), '%customer_');
|
|
9
|
+
assert.equal(likePattern(undefined), null);
|
|
10
|
+
});
|
|
11
|
+
//# sourceMappingURL=identifiers.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identifiers.test.js","sourceRoot":"","sources":["../../src/mysql/identifiers.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEhE,IAAI,CAAC,mCAAmC,EAAE,GAAG,EAAE;IAC7C,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,aAAa,CAAC,EAAE,gBAAgB,CAAC,CAAC;AACjE,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,uCAAuC,EAAE,GAAG,EAAE;IACjD,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,YAAY,CAAC,EAAE,YAAY,CAAC,CAAC;IACtD,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,CAAC;AAC7C,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { ColumnMetadata, ForeignKeyMetadata, IndexMetadata, PrimaryKeyMetadata, StatisticsMetadata } from '../core/schema-types.js';
|
|
2
|
+
interface TableRow {
|
|
3
|
+
schemaName: string;
|
|
4
|
+
tableName: string;
|
|
5
|
+
tableType: 'BASE TABLE' | 'VIEW';
|
|
6
|
+
createDate: Date | null;
|
|
7
|
+
rowCount: number | null;
|
|
8
|
+
}
|
|
9
|
+
export interface TableSearchResult {
|
|
10
|
+
schemaName: string;
|
|
11
|
+
tableName: string;
|
|
12
|
+
rowCount?: number;
|
|
13
|
+
createDate?: Date | null;
|
|
14
|
+
}
|
|
15
|
+
export interface ObjectSearchResult {
|
|
16
|
+
schemaName: string;
|
|
17
|
+
tableName?: string;
|
|
18
|
+
columnName?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface Relationship {
|
|
21
|
+
fromSchema: string;
|
|
22
|
+
fromTable: string;
|
|
23
|
+
fromColumn: string;
|
|
24
|
+
toSchema: string;
|
|
25
|
+
toTable: string;
|
|
26
|
+
toColumn: string;
|
|
27
|
+
constraintName: string;
|
|
28
|
+
deleteAction: string;
|
|
29
|
+
updateAction: string;
|
|
30
|
+
}
|
|
31
|
+
export declare function listTables(tableNames?: string[]): Promise<TableRow[]>;
|
|
32
|
+
export declare function getColumns(tableNames?: string[]): Promise<ColumnMetadata[]>;
|
|
33
|
+
export declare function getPrimaryKeys(tableNames?: string[]): Promise<(PrimaryKeyMetadata & {
|
|
34
|
+
tableName: string;
|
|
35
|
+
})[]>;
|
|
36
|
+
export declare function getForeignKeys(tableNames?: string[]): Promise<(ForeignKeyMetadata & {
|
|
37
|
+
tableName: string;
|
|
38
|
+
})[]>;
|
|
39
|
+
export declare function getIndexes(tableNames?: string[]): Promise<(IndexMetadata & {
|
|
40
|
+
tableName: string;
|
|
41
|
+
})[]>;
|
|
42
|
+
export declare function getStatistics(tableNames?: string[]): Promise<(StatisticsMetadata & {
|
|
43
|
+
tableName: string;
|
|
44
|
+
})[]>;
|
|
45
|
+
export declare function findTables(pattern?: string, hasColumn?: string): Promise<TableSearchResult[]>;
|
|
46
|
+
export declare function searchObjects(search: string, type?: string): Promise<ObjectSearchResult[]>;
|
|
47
|
+
export declare function getRelationships(): Promise<Relationship[]>;
|
|
48
|
+
export {};
|
|
49
|
+
//# sourceMappingURL=queries.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../../src/mysql/queries.ts"],"names":[],"mappings":"AACA,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,kBAAkB,EAClB,kBAAkB,EACnB,MAAM,yBAAyB,CAAC;AAIjC,UAAU,QAAQ;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,YAAY,GAAG,MAAM,CAAC;IACjC,UAAU,EAAE,IAAI,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,wBAAsB,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAoB3E;AAED,wBAAsB,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAoCjF;AAED,wBAAsB,cAAc,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,kBAAkB,GAAG;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,EAAE,CAAC,CAkBnH;AAED,wBAAsB,cAAc,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,kBAAkB,GAAG;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,EAAE,CAAC,CAmCnH;AAED,wBAAsB,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,aAAa,GAAG;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,EAAE,CAAC,CAoB1G;AAED,wBAAsB,aAAa,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,kBAAkB,GAAG;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,EAAE,CAAC,CAiBlH;AAED,wBAAsB,UAAU,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAyBnG;AAED,wBAAsB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CA8BhG;AAED,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC,CAuBhE"}
|