@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.
Files changed (149) hide show
  1. package/.env.example +35 -0
  2. package/.github/workflows/ci.yml +18 -0
  3. package/AUTHENTICATION.md +24 -0
  4. package/CLAUDE.md +37 -0
  5. package/CONTRIBUTING.md +19 -0
  6. package/LICENSE +21 -0
  7. package/QUERY_ACCESS_SETUP.md +65 -0
  8. package/README.md +144 -0
  9. package/SECURITY.md +10 -0
  10. package/TESTING.md +54 -0
  11. package/dist/cli.d.ts +3 -0
  12. package/dist/cli.d.ts.map +1 -0
  13. package/dist/cli.js +197 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/core/cache.d.ts +11 -0
  16. package/dist/core/cache.d.ts.map +1 -0
  17. package/dist/core/cache.js +30 -0
  18. package/dist/core/cache.js.map +1 -0
  19. package/dist/core/config.d.ts +24 -0
  20. package/dist/core/config.d.ts.map +1 -0
  21. package/dist/core/config.js +63 -0
  22. package/dist/core/config.js.map +1 -0
  23. package/dist/core/database-url.d.ts +11 -0
  24. package/dist/core/database-url.d.ts.map +1 -0
  25. package/dist/core/database-url.js +37 -0
  26. package/dist/core/database-url.js.map +1 -0
  27. package/dist/core/database-url.test.d.ts +2 -0
  28. package/dist/core/database-url.test.d.ts.map +1 -0
  29. package/dist/core/database-url.test.js +22 -0
  30. package/dist/core/database-url.test.js.map +1 -0
  31. package/dist/core/logger.d.ts +3 -0
  32. package/dist/core/logger.d.ts.map +1 -0
  33. package/dist/core/logger.js +17 -0
  34. package/dist/core/logger.js.map +1 -0
  35. package/dist/core/mcp.d.ts +14 -0
  36. package/dist/core/mcp.d.ts.map +1 -0
  37. package/dist/core/mcp.js +23 -0
  38. package/dist/core/mcp.js.map +1 -0
  39. package/dist/core/query-safety.d.ts +10 -0
  40. package/dist/core/query-safety.d.ts.map +1 -0
  41. package/dist/core/query-safety.js +50 -0
  42. package/dist/core/query-safety.js.map +1 -0
  43. package/dist/core/query-safety.test.d.ts +2 -0
  44. package/dist/core/query-safety.test.d.ts.map +1 -0
  45. package/dist/core/query-safety.test.js +30 -0
  46. package/dist/core/query-safety.test.js.map +1 -0
  47. package/dist/core/schema-types.d.ts +53 -0
  48. package/dist/core/schema-types.d.ts.map +1 -0
  49. package/dist/core/schema-types.js +2 -0
  50. package/dist/core/schema-types.js.map +1 -0
  51. package/dist/core/security/access-control.d.ts +32 -0
  52. package/dist/core/security/access-control.d.ts.map +1 -0
  53. package/dist/core/security/access-control.js +244 -0
  54. package/dist/core/security/access-control.js.map +1 -0
  55. package/dist/core/security/config-loader.d.ts +20 -0
  56. package/dist/core/security/config-loader.d.ts.map +1 -0
  57. package/dist/core/security/config-loader.js +227 -0
  58. package/dist/core/security/config-loader.js.map +1 -0
  59. package/dist/core/security/types.d.ts +64 -0
  60. package/dist/core/security/types.d.ts.map +1 -0
  61. package/dist/core/security/types.js +28 -0
  62. package/dist/core/security/types.js.map +1 -0
  63. package/dist/core/sql-parser.d.ts +23 -0
  64. package/dist/core/sql-parser.d.ts.map +1 -0
  65. package/dist/core/sql-parser.js +460 -0
  66. package/dist/core/sql-parser.js.map +1 -0
  67. package/dist/core/sql-parser.test.d.ts +2 -0
  68. package/dist/core/sql-parser.test.d.ts.map +1 -0
  69. package/dist/core/sql-parser.test.js +21 -0
  70. package/dist/core/sql-parser.test.js.map +1 -0
  71. package/dist/handlers/accessible-schema.d.ts +53 -0
  72. package/dist/handlers/accessible-schema.d.ts.map +1 -0
  73. package/dist/handlers/accessible-schema.js +192 -0
  74. package/dist/handlers/accessible-schema.js.map +1 -0
  75. package/dist/handlers/data.d.ts +17 -0
  76. package/dist/handlers/data.d.ts.map +1 -0
  77. package/dist/handlers/data.js +30 -0
  78. package/dist/handlers/data.js.map +1 -0
  79. package/dist/handlers/relationships.d.ts +14 -0
  80. package/dist/handlers/relationships.d.ts.map +1 -0
  81. package/dist/handlers/relationships.js +77 -0
  82. package/dist/handlers/relationships.js.map +1 -0
  83. package/dist/handlers/schema.d.ts +14 -0
  84. package/dist/handlers/schema.d.ts.map +1 -0
  85. package/dist/handlers/schema.js +104 -0
  86. package/dist/handlers/schema.js.map +1 -0
  87. package/dist/handlers/search.d.ts +14 -0
  88. package/dist/handlers/search.d.ts.map +1 -0
  89. package/dist/handlers/search.js +18 -0
  90. package/dist/handlers/search.js.map +1 -0
  91. package/dist/handlers/validation.d.ts +32 -0
  92. package/dist/handlers/validation.d.ts.map +1 -0
  93. package/dist/handlers/validation.js +116 -0
  94. package/dist/handlers/validation.js.map +1 -0
  95. package/dist/index.d.ts +3 -0
  96. package/dist/index.d.ts.map +1 -0
  97. package/dist/index.js +294 -0
  98. package/dist/index.js.map +1 -0
  99. package/dist/mysql/connection.d.ts +15 -0
  100. package/dist/mysql/connection.d.ts.map +1 -0
  101. package/dist/mysql/connection.js +57 -0
  102. package/dist/mysql/connection.js.map +1 -0
  103. package/dist/mysql/identifiers.d.ts +3 -0
  104. package/dist/mysql/identifiers.d.ts.map +1 -0
  105. package/dist/mysql/identifiers.js +10 -0
  106. package/dist/mysql/identifiers.js.map +1 -0
  107. package/dist/mysql/identifiers.test.d.ts +2 -0
  108. package/dist/mysql/identifiers.test.d.ts.map +1 -0
  109. package/dist/mysql/identifiers.test.js +11 -0
  110. package/dist/mysql/identifiers.test.js.map +1 -0
  111. package/dist/mysql/queries.d.ts +49 -0
  112. package/dist/mysql/queries.d.ts.map +1 -0
  113. package/dist/mysql/queries.js +206 -0
  114. package/dist/mysql/queries.js.map +1 -0
  115. package/docs/clients/claude-code.md +28 -0
  116. package/docs/clients/claude-desktop.md +35 -0
  117. package/docs/clients/codex.md +28 -0
  118. package/docs/clients/cursor.md +26 -0
  119. package/docs/clients/opencode.md +35 -0
  120. package/docs/clients/vscode.md +25 -0
  121. package/package.json +49 -0
  122. package/query-access.example.json +21 -0
  123. package/src/cli.ts +221 -0
  124. package/src/core/cache.ts +41 -0
  125. package/src/core/config.ts +97 -0
  126. package/src/core/database-url.test.ts +28 -0
  127. package/src/core/database-url.ts +47 -0
  128. package/src/core/logger.ts +24 -0
  129. package/src/core/mcp.ts +23 -0
  130. package/src/core/query-safety.test.ts +36 -0
  131. package/src/core/query-safety.ts +63 -0
  132. package/src/core/schema-types.ts +58 -0
  133. package/src/core/security/access-control.ts +321 -0
  134. package/src/core/security/config-loader.ts +315 -0
  135. package/src/core/security/types.ts +114 -0
  136. package/src/core/sql-parser.test.ts +37 -0
  137. package/src/core/sql-parser.ts +572 -0
  138. package/src/handlers/accessible-schema.ts +314 -0
  139. package/src/handlers/data.ts +66 -0
  140. package/src/handlers/relationships.ts +114 -0
  141. package/src/handlers/schema.ts +154 -0
  142. package/src/handlers/search.ts +34 -0
  143. package/src/handlers/validation.ts +165 -0
  144. package/src/index.ts +337 -0
  145. package/src/mysql/connection.ts +68 -0
  146. package/src/mysql/identifiers.test.ts +12 -0
  147. package/src/mysql/identifiers.ts +10 -0
  148. package/src/mysql/queries.ts +285 -0
  149. package/tsconfig.json +24 -0
@@ -0,0 +1,30 @@
1
+ class SchemaCache {
2
+ cache = new Map();
3
+ ttl = Number.parseInt(process.env.CACHE_TTL || '3600', 10) * 1000;
4
+ enabled = process.env.CACHE_ENABLED !== 'false';
5
+ get(key) {
6
+ if (!this.enabled) {
7
+ return null;
8
+ }
9
+ const entry = this.cache.get(key);
10
+ if (!entry) {
11
+ return null;
12
+ }
13
+ if (Date.now() - entry.timestamp > this.ttl) {
14
+ this.cache.delete(key);
15
+ return null;
16
+ }
17
+ return entry.data;
18
+ }
19
+ set(key, data) {
20
+ if (!this.enabled) {
21
+ return;
22
+ }
23
+ this.cache.set(key, { data, timestamp: Date.now() });
24
+ }
25
+ clear() {
26
+ this.cache.clear();
27
+ }
28
+ }
29
+ export const cache = new SchemaCache();
30
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/core/cache.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW;IACP,KAAK,GAAG,IAAI,GAAG,EAA+B,CAAC;IAC/C,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,MAAM,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC;IAClE,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,KAAK,OAAO,CAAC;IAExD,GAAG,CAAI,GAAW;QAChB,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAClB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAC5C,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACvB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,KAAK,CAAC,IAAS,CAAC;IACzB,CAAC;IAED,GAAG,CAAI,GAAW,EAAE,IAAO;QACzB,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAClB,OAAO;QACT,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IACvD,CAAC;IAED,KAAK;QACH,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;CACF;AAED,MAAM,CAAC,MAAM,KAAK,GAAG,IAAI,WAAW,EAAE,CAAC"}
@@ -0,0 +1,24 @@
1
+ export interface AppConfig {
2
+ db: {
3
+ host: string;
4
+ port: number;
5
+ name: string;
6
+ user: string;
7
+ password: string;
8
+ ssl: boolean;
9
+ };
10
+ server: {
11
+ name: string;
12
+ version: string;
13
+ schemaOnlyMode: boolean;
14
+ };
15
+ query: {
16
+ maxRows: number;
17
+ timeoutMs: number;
18
+ };
19
+ }
20
+ export declare function buildAppConfig(env?: NodeJS.ProcessEnv): AppConfig;
21
+ export declare const appConfig: AppConfig;
22
+ export declare function resolveDatabase(input?: string): string;
23
+ export declare function resolveSchema(input?: string): string;
24
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE;QACF,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,GAAG,EAAE,OAAO,CAAC;KACd,CAAC;IACF,MAAM,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,cAAc,EAAE,OAAO,CAAC;KACzB,CAAC;IACF,KAAK,EAAE;QACL,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAsBD,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,SAAS,CAsB9E;AAED,eAAO,MAAM,SAAS,EAAE,SAA4B,CAAC;AAErD,wBAAgB,eAAe,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAetD;AAED,wBAAgB,aAAa,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAQpD"}
@@ -0,0 +1,63 @@
1
+ import { config as dotenvConfig } from 'dotenv';
2
+ import { parseDatabaseUrl } from './database-url.js';
3
+ dotenvConfig();
4
+ function requiredEnv(env, name) {
5
+ const value = env[name];
6
+ if (!value) {
7
+ throw new Error(`${name} is required`);
8
+ }
9
+ return value;
10
+ }
11
+ function intEnv(env, name, defaultValue) {
12
+ const value = env[name];
13
+ if (!value) {
14
+ return defaultValue;
15
+ }
16
+ const parsed = Number.parseInt(value, 10);
17
+ if (Number.isNaN(parsed) || parsed <= 0) {
18
+ throw new Error(`${name} must be a positive integer`);
19
+ }
20
+ return parsed;
21
+ }
22
+ export function buildAppConfig(env = process.env) {
23
+ const urlConfig = env.DATABASE_URL ? parseDatabaseUrl(env.DATABASE_URL) : null;
24
+ return {
25
+ db: {
26
+ host: env.DB_HOST || urlConfig?.host || 'localhost',
27
+ port: intEnv(env, 'DB_PORT', urlConfig?.port || 3306),
28
+ name: env.DB_NAME || urlConfig?.name || requiredEnv(env, 'DB_NAME'),
29
+ user: env.DB_USER || urlConfig?.user || requiredEnv(env, 'DB_USER'),
30
+ password: env.DB_PASSWORD || urlConfig?.password || requiredEnv(env, 'DB_PASSWORD'),
31
+ ssl: env.DB_SSL ? env.DB_SSL === 'true' : Boolean(urlConfig?.ssl),
32
+ },
33
+ server: {
34
+ name: env.MCP_SERVER_NAME || 'mysql-mcp-server',
35
+ version: env.MCP_SERVER_VERSION || '1.0.0',
36
+ schemaOnlyMode: env.SCHEMA_ONLY_MODE !== 'false',
37
+ },
38
+ query: {
39
+ maxRows: intEnv(env, 'MAX_QUERY_ROWS', 100),
40
+ timeoutMs: intEnv(env, 'QUERY_TIMEOUT_MS', 30000),
41
+ },
42
+ };
43
+ }
44
+ export const appConfig = buildAppConfig();
45
+ export function resolveDatabase(input) {
46
+ if (input && /^\s*(SELECT|WITH)\b/i.test(input)) {
47
+ throw new Error('The SQL statement was passed as the database argument. ' +
48
+ 'Leave database blank and put the SQL in the execute_query query field.');
49
+ }
50
+ if (input && input !== appConfig.db.name) {
51
+ throw new Error(`This server is configured for database '${appConfig.db.name}'. ` +
52
+ `Received '${input}'. Start a separate MCP server instance for another database.`);
53
+ }
54
+ return appConfig.db.name;
55
+ }
56
+ export function resolveSchema(input) {
57
+ if (input && input !== appConfig.db.name) {
58
+ throw new Error(`MySQL uses the configured database as the schema for this server. ` +
59
+ `Received schema '${input}', expected '${appConfig.db.name}'.`);
60
+ }
61
+ return appConfig.db.name;
62
+ }
63
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export interface DatabaseUrlConfig {
2
+ host: string;
3
+ port: number;
4
+ name: string;
5
+ user: string;
6
+ password: string;
7
+ ssl?: boolean;
8
+ }
9
+ export declare function parseDatabaseUrl(value: string): DatabaseUrlConfig;
10
+ export declare function maskDatabaseUrl(value: string): string;
11
+ //# sourceMappingURL=database-url.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database-url.d.ts","sourceRoot":"","sources":["../../src/core/database-url.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,iBAAiB,CAyBjE;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAUrD"}
@@ -0,0 +1,37 @@
1
+ export function parseDatabaseUrl(value) {
2
+ let url;
3
+ try {
4
+ url = new URL(value);
5
+ }
6
+ catch {
7
+ throw new Error('DATABASE_URL must be a valid MySQL URL');
8
+ }
9
+ if (url.protocol !== 'mysql:' && url.protocol !== 'mysql2:') {
10
+ throw new Error('DATABASE_URL must start with mysql://');
11
+ }
12
+ const name = decodeURIComponent(url.pathname.replace(/^\//, ''));
13
+ if (!name) {
14
+ throw new Error('DATABASE_URL must include a database name, for example mysql://user:pass@host:3306/app_db');
15
+ }
16
+ return {
17
+ host: url.hostname || 'localhost',
18
+ port: url.port ? Number.parseInt(url.port, 10) : 3306,
19
+ name,
20
+ user: decodeURIComponent(url.username),
21
+ password: decodeURIComponent(url.password),
22
+ ssl: url.searchParams.get('ssl') === 'true' || url.searchParams.get('ssl-mode') === 'REQUIRED',
23
+ };
24
+ }
25
+ export function maskDatabaseUrl(value) {
26
+ try {
27
+ const url = new URL(value);
28
+ if (url.password) {
29
+ url.password = '********';
30
+ }
31
+ return url.toString();
32
+ }
33
+ catch {
34
+ return '<invalid DATABASE_URL>';
35
+ }
36
+ }
37
+ //# sourceMappingURL=database-url.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database-url.js","sourceRoot":"","sources":["../../src/core/database-url.ts"],"names":[],"mappings":"AASA,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAC5D,CAAC;IAED,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,IAAI,GAAG,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC5D,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC3D,CAAC;IAED,MAAM,IAAI,GAAG,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC;IACjE,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,2FAA2F,CAAC,CAAC;IAC/G,CAAC;IAED,OAAO;QACL,IAAI,EAAE,GAAG,CAAC,QAAQ,IAAI,WAAW;QACjC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI;QACrD,IAAI;QACJ,IAAI,EAAE,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC;QACtC,QAAQ,EAAE,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC;QAC1C,GAAG,EAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,MAAM,IAAI,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,UAAU;KAC/F,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;QAC3B,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;YACjB,GAAG,CAAC,QAAQ,GAAG,UAAU,CAAC;QAC5B,CAAC;QACD,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,wBAAwB,CAAC;IAClC,CAAC;AACH,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=database-url.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database-url.test.d.ts","sourceRoot":"","sources":["../../src/core/database-url.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,22 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+ import { maskDatabaseUrl, parseDatabaseUrl } from './database-url.js';
4
+ test('parseDatabaseUrl parses MySQL connection strings', () => {
5
+ const result = parseDatabaseUrl('mysql://mcp_reader:s3cret@db.example.com:3307/app_db?ssl=true');
6
+ assert.deepEqual(result, {
7
+ host: 'db.example.com',
8
+ port: 3307,
9
+ name: 'app_db',
10
+ user: 'mcp_reader',
11
+ password: 's3cret',
12
+ ssl: true,
13
+ });
14
+ });
15
+ test('parseDatabaseUrl requires a MySQL protocol and database name', () => {
16
+ assert.throws(() => parseDatabaseUrl('postgres://user:pass@localhost/app_db'), /mysql/);
17
+ assert.throws(() => parseDatabaseUrl('mysql://user:pass@localhost'), /database name/);
18
+ });
19
+ test('maskDatabaseUrl hides passwords', () => {
20
+ assert.equal(maskDatabaseUrl('mysql://mcp_reader:s3cret@localhost:3306/app_db'), 'mysql://mcp_reader:********@localhost:3306/app_db');
21
+ });
22
+ //# sourceMappingURL=database-url.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database-url.test.js","sourceRoot":"","sources":["../../src/core/database-url.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAEtE,IAAI,CAAC,kDAAkD,EAAE,GAAG,EAAE;IAC5D,MAAM,MAAM,GAAG,gBAAgB,CAAC,+DAA+D,CAAC,CAAC;IAEjG,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE;QACvB,IAAI,EAAE,gBAAgB;QACtB,IAAI,EAAE,IAAI;QACV,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,YAAY;QAClB,QAAQ,EAAE,QAAQ;QAClB,GAAG,EAAE,IAAI;KACV,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,8DAA8D,EAAE,GAAG,EAAE;IACxE,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,gBAAgB,CAAC,uCAAuC,CAAC,EAAE,OAAO,CAAC,CAAC;IACxF,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,gBAAgB,CAAC,6BAA6B,CAAC,EAAE,eAAe,CAAC,CAAC;AACxF,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,iCAAiC,EAAE,GAAG,EAAE;IAC3C,MAAM,CAAC,KAAK,CACV,eAAe,CAAC,iDAAiD,CAAC,EAClE,mDAAmD,CACpD,CAAC;AACJ,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import winston from 'winston';
2
+ export declare const logger: winston.Logger;
3
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/core/logger.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,eAAO,MAAM,MAAM,gBAYjB,CAAC"}
@@ -0,0 +1,17 @@
1
+ import winston from 'winston';
2
+ export const logger = winston.createLogger({
3
+ level: process.env.LOG_LEVEL || 'info',
4
+ format: winston.format.combine(winston.format.timestamp(), winston.format.errors({ stack: true }), winston.format.json()),
5
+ transports: [
6
+ new winston.transports.File({
7
+ filename: process.env.LOG_FILE || 'mcp-server.log',
8
+ }),
9
+ ],
10
+ });
11
+ if (process.env.NODE_ENV !== 'production') {
12
+ logger.add(new winston.transports.Console({
13
+ format: winston.format.simple(),
14
+ stderrLevels: ['error', 'warn', 'info', 'debug'],
15
+ }));
16
+ }
17
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/core/logger.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,MAAM,CAAC,MAAM,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IACzC,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,MAAM;IACtC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO,CAC5B,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,EAC1B,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EACtC,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CACtB;IACD,UAAU,EAAE;QACV,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC;YAC1B,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,gBAAgB;SACnD,CAAC;KACH;CACF,CAAC,CAAC;AAEH,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,EAAE,CAAC;IAC1C,MAAM,CAAC,GAAG,CACR,IAAI,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC;QAC7B,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE;QAC/B,YAAY,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC;KACjD,CAAC,CACH,CAAC;AACJ,CAAC"}
@@ -0,0 +1,14 @@
1
+ export declare function textResult(value: unknown): {
2
+ content: {
3
+ type: "text";
4
+ text: string;
5
+ }[];
6
+ };
7
+ export declare function errorResult(error: unknown): {
8
+ content: {
9
+ type: "text";
10
+ text: string;
11
+ }[];
12
+ isError: boolean;
13
+ };
14
+ //# sourceMappingURL=mcp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../src/core/mcp.ts"],"names":[],"mappings":"AAAA,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO;;;;;EASxC;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO;;;;;;EAWzC"}
@@ -0,0 +1,23 @@
1
+ export function textResult(value) {
2
+ return {
3
+ content: [
4
+ {
5
+ type: 'text',
6
+ text: JSON.stringify(value, null, 2),
7
+ },
8
+ ],
9
+ };
10
+ }
11
+ export function errorResult(error) {
12
+ const message = error instanceof Error ? error.message : String(error);
13
+ return {
14
+ content: [
15
+ {
16
+ type: 'text',
17
+ text: `Error: ${message}`,
18
+ },
19
+ ],
20
+ isError: true,
21
+ };
22
+ }
23
+ //# sourceMappingURL=mcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.js","sourceRoot":"","sources":["../../src/core/mcp.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,OAAO;QACL,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAe;gBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;aACrC;SACF;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvE,OAAO;QACL,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAe;gBACrB,IAAI,EAAE,UAAU,OAAO,EAAE;aAC1B;SACF;QACD,OAAO,EAAE,IAAI;KACd,CAAC;AACJ,CAAC"}
@@ -0,0 +1,10 @@
1
+ export interface QueryModificationResult {
2
+ wasModified: boolean;
3
+ modifiedQuery: string;
4
+ modifications: string[];
5
+ originalLimitValue?: number;
6
+ appliedLimitValue: number;
7
+ }
8
+ export declare function validateQuerySafety(query: string): void;
9
+ export declare function enforceRowLimit(query: string, maxRows: number): QueryModificationResult;
10
+ //# sourceMappingURL=query-safety.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-safety.d.ts","sourceRoot":"","sources":["../../src/core/query-safety.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,uBAAuB;IACtC,WAAW,EAAE,OAAO,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAiBvD;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,uBAAuB,CAmCvF"}
@@ -0,0 +1,50 @@
1
+ export function validateQuerySafety(query) {
2
+ const normalizedQuery = query.trim().toUpperCase();
3
+ const dangerousPatterns = [
4
+ { pattern: /\b(INSERT|UPDATE|DELETE|TRUNCATE|DROP|CREATE|ALTER|CALL|GRANT|REVOKE)\b/i, type: 'DML/DDL' },
5
+ { pattern: /\b(LOAD\s+DATA|INTO\s+OUTFILE|INTO\s+DUMPFILE)\b/i, type: 'file access' },
6
+ { pattern: /\bLOCK\s+TABLES\b/i, type: 'table lock' },
7
+ ];
8
+ for (const { pattern, type } of dangerousPatterns) {
9
+ if (pattern.test(query)) {
10
+ throw new Error(`Query contains forbidden ${type} operation. Only read-only SELECT queries are allowed.`);
11
+ }
12
+ }
13
+ if (!normalizedQuery.startsWith('SELECT') && !normalizedQuery.startsWith('WITH')) {
14
+ throw new Error('Query must start with SELECT or WITH. Only read-only SELECT queries are allowed.');
15
+ }
16
+ }
17
+ export function enforceRowLimit(query, maxRows) {
18
+ const trimmedQuery = query.trim().replace(/;+\s*$/, '');
19
+ const modifications = [];
20
+ const limitPattern = /\s+LIMIT\s+(\d+)(\s*,\s*\d+|\s+OFFSET\s+\d+)?\s*$/i;
21
+ const limitMatch = trimmedQuery.match(limitPattern);
22
+ if (!limitMatch) {
23
+ modifications.push(`Added LIMIT ${maxRows} for safety`);
24
+ return {
25
+ wasModified: true,
26
+ modifiedQuery: `${trimmedQuery} LIMIT ${maxRows}`,
27
+ modifications,
28
+ appliedLimitValue: maxRows,
29
+ };
30
+ }
31
+ const originalLimitValue = Number.parseInt(limitMatch[1], 10);
32
+ if (originalLimitValue > maxRows) {
33
+ modifications.push(`Reduced LIMIT from ${originalLimitValue} to ${maxRows} (safety maximum)`);
34
+ return {
35
+ wasModified: true,
36
+ modifiedQuery: trimmedQuery.replace(limitPattern, ` LIMIT ${maxRows}`),
37
+ modifications,
38
+ originalLimitValue,
39
+ appliedLimitValue: maxRows,
40
+ };
41
+ }
42
+ return {
43
+ wasModified: false,
44
+ modifiedQuery: trimmedQuery,
45
+ modifications,
46
+ originalLimitValue,
47
+ appliedLimitValue: originalLimitValue,
48
+ };
49
+ }
50
+ //# sourceMappingURL=query-safety.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-safety.js","sourceRoot":"","sources":["../../src/core/query-safety.ts"],"names":[],"mappings":"AAQA,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,MAAM,eAAe,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACnD,MAAM,iBAAiB,GAAG;QACxB,EAAE,OAAO,EAAE,0EAA0E,EAAE,IAAI,EAAE,SAAS,EAAE;QACxG,EAAE,OAAO,EAAE,mDAAmD,EAAE,IAAI,EAAE,aAAa,EAAE;QACrF,EAAE,OAAO,EAAE,oBAAoB,EAAE,IAAI,EAAE,YAAY,EAAE;KACtD,CAAC;IAEF,KAAK,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,iBAAiB,EAAE,CAAC;QAClD,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,4BAA4B,IAAI,wDAAwD,CAAC,CAAC;QAC5G,CAAC;IACH,CAAC;IAED,IAAI,CAAC,eAAe,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,eAAe,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACjF,MAAM,IAAI,KAAK,CAAC,kFAAkF,CAAC,CAAC;IACtG,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAa,EAAE,OAAe;IAC5D,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IACxD,MAAM,aAAa,GAAa,EAAE,CAAC;IACnC,MAAM,YAAY,GAAG,oDAAoD,CAAC;IAC1E,MAAM,UAAU,GAAG,YAAY,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IAEpD,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,aAAa,CAAC,IAAI,CAAC,eAAe,OAAO,aAAa,CAAC,CAAC;QACxD,OAAO;YACL,WAAW,EAAE,IAAI;YACjB,aAAa,EAAE,GAAG,YAAY,UAAU,OAAO,EAAE;YACjD,aAAa;YACb,iBAAiB,EAAE,OAAO;SAC3B,CAAC;IACJ,CAAC;IAED,MAAM,kBAAkB,GAAG,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC9D,IAAI,kBAAkB,GAAG,OAAO,EAAE,CAAC;QACjC,aAAa,CAAC,IAAI,CAAC,sBAAsB,kBAAkB,OAAO,OAAO,mBAAmB,CAAC,CAAC;QAC9F,OAAO;YACL,WAAW,EAAE,IAAI;YACjB,aAAa,EAAE,YAAY,CAAC,OAAO,CAAC,YAAY,EAAE,UAAU,OAAO,EAAE,CAAC;YACtE,aAAa;YACb,kBAAkB;YAClB,iBAAiB,EAAE,OAAO;SAC3B,CAAC;IACJ,CAAC;IAED,OAAO;QACL,WAAW,EAAE,KAAK;QAClB,aAAa,EAAE,YAAY;QAC3B,aAAa;QACb,kBAAkB;QAClB,iBAAiB,EAAE,kBAAkB;KACtC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=query-safety.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-safety.test.d.ts","sourceRoot":"","sources":["../../src/core/query-safety.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,30 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+ import { enforceRowLimit, validateQuerySafety } from './query-safety.js';
4
+ test('validateQuerySafety allows SELECT and WITH queries', () => {
5
+ assert.doesNotThrow(() => validateQuerySafety('SELECT id, email FROM customers'));
6
+ assert.doesNotThrow(() => validateQuerySafety('WITH recent_orders AS (SELECT id FROM orders) SELECT id FROM recent_orders'));
7
+ });
8
+ test('validateQuerySafety blocks write and file operations', () => {
9
+ assert.throws(() => validateQuerySafety('DELETE FROM customers'), /forbidden/);
10
+ assert.throws(() => validateQuerySafety('SELECT * INTO OUTFILE "/tmp/x" FROM customers'), /forbidden/);
11
+ });
12
+ test('enforceRowLimit appends a LIMIT when missing', () => {
13
+ const result = enforceRowLimit('SELECT id FROM customers ORDER BY id', 100);
14
+ assert.equal(result.wasModified, true);
15
+ assert.equal(result.modifiedQuery, 'SELECT id FROM customers ORDER BY id LIMIT 100');
16
+ assert.equal(result.appliedLimitValue, 100);
17
+ });
18
+ test('enforceRowLimit reduces an excessive LIMIT', () => {
19
+ const result = enforceRowLimit('SELECT id FROM customers LIMIT 500', 100);
20
+ assert.equal(result.wasModified, true);
21
+ assert.equal(result.modifiedQuery, 'SELECT id FROM customers LIMIT 100');
22
+ assert.equal(result.originalLimitValue, 500);
23
+ });
24
+ test('enforceRowLimit keeps an acceptable LIMIT', () => {
25
+ const result = enforceRowLimit('SELECT id FROM customers LIMIT 25', 100);
26
+ assert.equal(result.wasModified, false);
27
+ assert.equal(result.modifiedQuery, 'SELECT id FROM customers LIMIT 25');
28
+ assert.equal(result.appliedLimitValue, 25);
29
+ });
30
+ //# sourceMappingURL=query-safety.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-safety.test.js","sourceRoot":"","sources":["../../src/core/query-safety.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAEzE,IAAI,CAAC,oDAAoD,EAAE,GAAG,EAAE;IAC9D,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC,mBAAmB,CAAC,iCAAiC,CAAC,CAAC,CAAC;IAClF,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,CACvB,mBAAmB,CAAC,4EAA4E,CAAC,CAClG,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,sDAAsD,EAAE,GAAG,EAAE;IAChE,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,mBAAmB,CAAC,uBAAuB,CAAC,EAAE,WAAW,CAAC,CAAC;IAC/E,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,mBAAmB,CAAC,+CAA+C,CAAC,EAAE,WAAW,CAAC,CAAC;AACzG,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,8CAA8C,EAAE,GAAG,EAAE;IACxD,MAAM,MAAM,GAAG,eAAe,CAAC,sCAAsC,EAAE,GAAG,CAAC,CAAC;IAC5E,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;IACvC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,aAAa,EAAE,gDAAgD,CAAC,CAAC;IACrF,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAC;AAC9C,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,4CAA4C,EAAE,GAAG,EAAE;IACtD,MAAM,MAAM,GAAG,eAAe,CAAC,oCAAoC,EAAE,GAAG,CAAC,CAAC;IAC1E,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;IACvC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,aAAa,EAAE,oCAAoC,CAAC,CAAC;IACzE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC;AAC/C,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,2CAA2C,EAAE,GAAG,EAAE;IACrD,MAAM,MAAM,GAAG,eAAe,CAAC,mCAAmC,EAAE,GAAG,CAAC,CAAC;IACzE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IACxC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,aAAa,EAAE,mCAAmC,CAAC,CAAC;IACxE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC;AAC7C,CAAC,CAAC,CAAC"}
@@ -0,0 +1,53 @@
1
+ export interface ColumnMetadata {
2
+ name: string;
3
+ ordinal: number;
4
+ dataType: string;
5
+ nullable: boolean;
6
+ isIdentity: boolean;
7
+ isComputed: boolean;
8
+ defaultValue?: string | null;
9
+ description?: string | null;
10
+ isPrimaryKey: boolean;
11
+ isForeignKey: boolean;
12
+ }
13
+ export interface PrimaryKeyMetadata {
14
+ constraintName: string;
15
+ columns: string;
16
+ }
17
+ export interface ForeignKeyMetadata {
18
+ constraintName: string;
19
+ fromSchema: string;
20
+ fromTable: string;
21
+ fromColumns: string;
22
+ toSchema: string;
23
+ toTable: string;
24
+ toColumns: string;
25
+ onDelete: string;
26
+ onUpdate: string;
27
+ }
28
+ export interface IndexMetadata {
29
+ name: string;
30
+ type: string;
31
+ isUnique: boolean;
32
+ isPrimaryKey: boolean;
33
+ columns: string;
34
+ }
35
+ export interface StatisticsMetadata {
36
+ rowCount?: number;
37
+ totalSizeKB?: number;
38
+ usedSizeKB?: number;
39
+ }
40
+ export interface TableMetadata {
41
+ schema: string;
42
+ name: string;
43
+ type: 'TABLE' | 'VIEW';
44
+ columns: ColumnMetadata[];
45
+ primaryKey?: PrimaryKeyMetadata;
46
+ foreignKeys?: ForeignKeyMetadata[];
47
+ indexes: IndexMetadata[];
48
+ statistics?: StatisticsMetadata;
49
+ }
50
+ export interface SchemaResult {
51
+ schema: TableMetadata[];
52
+ }
53
+ //# sourceMappingURL=schema-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-types.d.ts","sourceRoot":"","sources":["../../src/core/schema-types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,EAAE,OAAO,CAAC;IACtB,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,YAAY,EAAE,OAAO,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC;IACvB,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,UAAU,CAAC,EAAE,kBAAkB,CAAC;IAChC,WAAW,CAAC,EAAE,kBAAkB,EAAE,CAAC;IACnC,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,UAAU,CAAC,EAAE,kBAAkB,CAAC;CACjC;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,aAAa,EAAE,CAAC;CACzB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=schema-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-types.js","sourceRoot":"","sources":["../../src/core/schema-types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Access Control Validation for execute_query
3
+ *
4
+ * Validates SQL queries against access control configuration:
5
+ * - Database access (must be configured)
6
+ * - Table whitelist/blacklist
7
+ * - Column access (inclusion/exclusion modes)
8
+ * - SELECT * blocking
9
+ */
10
+ import { AccessControlConfig } from "./types.js";
11
+ /**
12
+ * Validate a query against access control configuration
13
+ * @throws AccessControlError if validation fails
14
+ */
15
+ export declare function validateQueryAccess(query: string, database: string, config: AccessControlConfig): void;
16
+ /**
17
+ * Initialize the global access control config
18
+ * Called once at startup from index.ts
19
+ */
20
+ export declare function initAccessControl(config: AccessControlConfig): void;
21
+ /**
22
+ * Get the global access control config
23
+ * @throws Error if not initialized
24
+ */
25
+ export declare function getAccessControlConfig(): AccessControlConfig;
26
+ /**
27
+ * Check if access control is initialized
28
+ */
29
+ export declare function isAccessControlInitialized(): boolean;
30
+ export { AccessControlConfig, AccessControlError } from "./types.js";
31
+ export { loadAccessControlConfig, getTableConfigForSchema, } from "./config-loader.js";
32
+ //# sourceMappingURL=access-control.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"access-control.d.ts","sourceRoot":"","sources":["../../../src/core/security/access-control.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,mBAAmB,EAKpB,MAAM,YAAY,CAAC;AAKpB;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,mBAAmB,GAC1B,IAAI,CAiDN;AAgND;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI,CAGnE;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,mBAAmB,CAO5D;AAED;;GAEG;AACH,wBAAgB,0BAA0B,IAAI,OAAO,CAEpD;AAGD,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACrE,OAAO,EACL,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,oBAAoB,CAAC"}