@conte777/db-view-mcp 1.2.0 → 1.3.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 (57) hide show
  1. package/README.md +34 -250
  2. package/config.example.json +3 -1
  3. package/dist/config/types.d.ts +14 -4
  4. package/dist/config/types.js +5 -10
  5. package/dist/config/types.js.map +1 -1
  6. package/dist/connectors/clickhouse.d.ts +1 -1
  7. package/dist/connectors/clickhouse.js +11 -3
  8. package/dist/connectors/clickhouse.js.map +1 -1
  9. package/dist/connectors/manager.d.ts +7 -2
  10. package/dist/connectors/manager.js +38 -35
  11. package/dist/connectors/manager.js.map +1 -1
  12. package/dist/connectors/postgresql.d.ts +1 -1
  13. package/dist/connectors/postgresql.js +61 -13
  14. package/dist/connectors/postgresql.js.map +1 -1
  15. package/dist/index.js +0 -0
  16. package/dist/server.d.ts +1 -1
  17. package/dist/server.js +6 -2
  18. package/dist/server.js.map +1 -1
  19. package/dist/tools/readonly/describe-table.d.ts +1 -3
  20. package/dist/tools/readonly/describe-table.js +5 -5
  21. package/dist/tools/readonly/describe-table.js.map +1 -1
  22. package/dist/tools/readonly/explain.d.ts +1 -3
  23. package/dist/tools/readonly/explain.js +6 -6
  24. package/dist/tools/readonly/explain.js.map +1 -1
  25. package/dist/tools/readonly/list-tables.d.ts +1 -3
  26. package/dist/tools/readonly/list-tables.js +5 -5
  27. package/dist/tools/readonly/list-tables.js.map +1 -1
  28. package/dist/tools/readonly/performance.d.ts +1 -3
  29. package/dist/tools/readonly/performance.js +9 -6
  30. package/dist/tools/readonly/performance.js.map +1 -1
  31. package/dist/tools/readonly/query.d.ts +1 -3
  32. package/dist/tools/readonly/query.js +14 -7
  33. package/dist/tools/readonly/query.js.map +1 -1
  34. package/dist/tools/readonly/schema.d.ts +1 -3
  35. package/dist/tools/readonly/schema.js +5 -5
  36. package/dist/tools/readonly/schema.js.map +1 -1
  37. package/dist/tools/write/execute.d.ts +5 -3
  38. package/dist/tools/write/execute.js +14 -6
  39. package/dist/tools/write/execute.js.map +1 -1
  40. package/dist/tools/write/transaction.d.ts +3 -4
  41. package/dist/tools/write/transaction.js +33 -24
  42. package/dist/tools/write/transaction.js.map +1 -1
  43. package/dist/transport/http.d.ts +2 -2
  44. package/dist/transport/http.js +23 -9
  45. package/dist/transport/http.js.map +1 -1
  46. package/dist/utils/logger.js +1 -1
  47. package/dist/utils/logger.js.map +1 -1
  48. package/dist/utils/resolve-db.d.ts +5 -0
  49. package/dist/utils/resolve-db.js +61 -0
  50. package/dist/utils/resolve-db.js.map +1 -0
  51. package/dist/utils/response.d.ts +12 -0
  52. package/dist/utils/response.js +130 -3
  53. package/dist/utils/response.js.map +1 -1
  54. package/dist/utils/sql-validator.d.ts +16 -0
  55. package/dist/utils/sql-validator.js +157 -4
  56. package/dist/utils/sql-validator.js.map +1 -1
  57. package/package.json +9 -1
package/README.md CHANGED
@@ -1,23 +1,22 @@
1
1
  # db-view-mcp
2
2
 
3
- MCP server that gives AI assistants direct access to PostgreSQL and ClickHouse databases. Supports stdio and HTTP transports, allowing both local IDE integration and remote network access.
3
+ An MCP server that gives AI assistants direct access to PostgreSQL and ClickHouse databases. It
4
+ supports both stdio and HTTP transports, for local IDE integration and remote network access.
4
5
 
5
6
  ## Features
6
7
 
7
- - **Multi-database** — connect to any number of PostgreSQL and ClickHouse instances simultaneously
8
+ - **Multi-database** — connect any number of PostgreSQL and ClickHouse instances at once
8
9
  - **Dual transport** — stdio for IDE integration (Cursor, Claude Code), HTTP for remote/multi-client access
9
- - **Read & write tools** — SELECT queries with row limits, INSERT/UPDATE/DELETE, DDL, transactions
10
+ - **Read & write tools** — SELECT with row limits, INSERT/UPDATE/DELETE, DDL, transactions
10
11
  - **Schema introspection** — list tables, describe columns, export full DDL
11
- - **Query analysis** — EXPLAIN ANALYZE support, slow query tracking
12
- - **SQL safety** — read-only tools validate SQL to block accidental writes
13
- - **Flexible tool modes** — single tool with `database` parameter, or separate tool per database
12
+ - **Query analysis** — EXPLAIN / EXPLAIN ANALYZE, slow-query tracking
13
+ - **SQL safety** — read-only tools validate SQL and block accidental writes
14
+ - **Flexible tool modes** — one tool with a `database` parameter, or a separate tool per database
14
15
  - **Lazy connections** — databases connect on first use by default
15
- - **Bearer auth** — optional token-based authentication for HTTP transport
16
+ - **Bearer auth** — optional token authentication for the HTTP transport
16
17
  - **Session management** — stateful (per-session MCP server) or stateless HTTP mode
17
18
 
18
- ## Quick start
19
-
20
- ### Install
19
+ ## Install
21
20
 
22
21
  ```bash
23
22
  npm install @conte777/db-view-mcp
@@ -32,7 +31,9 @@ npm install
32
31
  npm run build
33
32
  ```
34
33
 
35
- ### Configure
34
+ ## Quick start
35
+
36
+ ### 1. Configure
36
37
 
37
38
  Copy the example config and edit it:
38
39
 
@@ -40,7 +41,7 @@ Copy the example config and edit it:
40
41
  cp config.example.json config.json
41
42
  ```
42
43
 
43
- Minimal config (stdio, default):
44
+ Minimal stdio config:
44
45
 
45
46
  ```json
46
47
  {
@@ -58,66 +59,25 @@ Minimal config (stdio, default):
58
59
  }
59
60
  ```
60
61
 
61
- HTTP transport config:
62
+ Every field, default, and the full config reference (SSL/TLS, per-database overrides, environment
63
+ substitution, HTTP transport) is in [docs/configuration.md](docs/configuration.md).
62
64
 
63
- ```json
64
- {
65
- "transport": {
66
- "type": "http",
67
- "port": 3000,
68
- "host": "127.0.0.1",
69
- "stateless": false,
70
- "auth": {
71
- "type": "bearer",
72
- "token": "your-secret-token"
73
- }
74
- },
75
- "databases": [
76
- {
77
- "id": "main_pg",
78
- "type": "postgresql",
79
- "host": "localhost",
80
- "port": 5432,
81
- "database": "myapp",
82
- "user": "admin",
83
- "password": "secret123"
84
- }
85
- ]
86
- }
87
- ```
88
-
89
- ### Run
65
+ ### 2. Run
90
66
 
91
67
  ```bash
92
68
  # Stdio (default)
93
69
  npm start -- --config config.json
94
70
 
95
- # HTTP via config (set transport.type to "http" in config.json)
96
- npm start -- --config config.json
97
-
98
- # HTTP via CLI flag (overrides config)
71
+ # HTTP (set transport.type to "http" in config, or override on the CLI)
99
72
  npm start -- --config config.json --transport http
100
73
 
101
74
  # Development (no build needed)
102
75
  npm run dev -- --config config.json
103
76
  ```
104
77
 
105
- ### Add to your MCP client
106
-
107
- **Claude Desktop** (`claude_desktop_config.json`) — stdio:
108
-
109
- ```json
110
- {
111
- "mcpServers": {
112
- "database": {
113
- "command": "npx",
114
- "args": ["-y", "@conte777/db-view-mcp", "--config", "/path/to/config.json"]
115
- }
116
- }
117
- }
118
- ```
78
+ ### 3. Add an MCP client
119
79
 
120
- **Claude Code** (`.mcp.json`) stdio:
80
+ **Claude Desktop** (`claude_desktop_config.json`) or **Claude Code** (`.mcp.json`), stdio:
121
81
 
122
82
  ```json
123
83
  {
@@ -130,211 +90,35 @@ npm run dev -- --config config.json
130
90
  }
131
91
  ```
132
92
 
133
- **Any MCP client** HTTP:
93
+ **Any MCP client**, HTTP:
134
94
 
135
95
  ```bash
136
- # Start the server
137
96
  node dist/index.js --config config.json --transport http
138
97
  # Server listens on http://127.0.0.1:3000/mcp
139
98
  ```
140
99
 
141
- ## Transport modes
142
-
143
- ### Stdio (default)
144
-
145
- Communication via stdin/stdout. Best for local IDE integrations where the MCP client spawns the server process.
146
-
147
- ### HTTP
148
-
149
- Uses the MCP Streamable HTTP transport (`POST/GET/DELETE /mcp`). Best for:
150
- - Remote access over the network
151
- - Multiple clients connecting simultaneously
152
- - Web application integrations
153
-
154
- **Stateful mode** (default): each MCP session gets its own `McpServer` instance with a unique session ID. All sessions share database connection pools. Supports transactions across requests within the same session.
155
-
156
- **Stateless mode** (`"stateless": true`): no session management. Each request is independent. Suitable for simple query scenarios without transactions.
157
-
158
- #### HTTP endpoints
100
+ See [docs/http-transport.md](docs/http-transport.md) for sessions, auth, and curl examples.
159
101
 
160
- | Method | Path | Description |
161
- |--------|------|-------------|
162
- | `POST` | `/mcp` | Send JSON-RPC requests (initialize, tools/call, etc.) |
163
- | `GET` | `/mcp` | SSE stream for server-to-client notifications |
164
- | `DELETE` | `/mcp` | Close a session |
165
- | `GET` | `/health` | Health check — status, active sessions, database list |
102
+ ## Documentation
166
103
 
167
- #### Authentication
168
-
169
- Optional bearer token authentication:
170
-
171
- ```json
172
- {
173
- "transport": {
174
- "type": "http",
175
- "auth": {
176
- "type": "bearer",
177
- "token": "your-secret-token"
178
- }
179
- }
180
- }
181
- ```
182
-
183
- Requests to `/mcp` must include `Authorization: Bearer your-secret-token`. Requests without a valid token receive `401 Unauthorized`. The `/health` endpoint is not protected.
184
-
185
- #### Example: initialize a session
186
-
187
- ```bash
188
- curl -X POST http://localhost:3000/mcp \
189
- -H "Content-Type: application/json" \
190
- -H "Accept: application/json, text/event-stream" \
191
- -H "Authorization: Bearer your-secret-token" \
192
- -d '{
193
- "jsonrpc": "2.0",
194
- "id": 1,
195
- "method": "initialize",
196
- "params": {
197
- "protocolVersion": "2025-03-26",
198
- "capabilities": {},
199
- "clientInfo": { "name": "test", "version": "1.0" }
200
- }
201
- }'
202
- ```
203
-
204
- The response includes a `Mcp-Session-Id` header. Use it in subsequent requests:
205
-
206
- ```bash
207
- curl -X POST http://localhost:3000/mcp \
208
- -H "Content-Type: application/json" \
209
- -H "Accept: application/json, text/event-stream" \
210
- -H "Authorization: Bearer your-secret-token" \
211
- -H "Mcp-Session-Id: <session-id-from-init>" \
212
- -d '{
213
- "jsonrpc": "2.0",
214
- "id": 2,
215
- "method": "tools/list",
216
- "params": {}
217
- }'
218
- ```
219
-
220
- ## Tools
221
-
222
- ### Read-only
223
-
224
- | Tool | Description |
225
- |------|-------------|
226
- | `query` | Execute a SELECT query with automatic row limit |
227
- | `list_databases` | List all configured database connections |
228
- | `list_tables` | List tables and views in a schema |
229
- | `describe_table` | Get column names, types, nullability, and primary keys |
230
- | `schema` | Export full DDL for a database |
231
- | `explain_query` | Run EXPLAIN ANALYZE (PostgreSQL) or EXPLAIN (ClickHouse) |
232
- | `performance` | Track and retrieve slow queries, set thresholds |
233
-
234
- ### Write
235
-
236
- | Tool | Description |
237
- |------|-------------|
238
- | `execute` | Run INSERT, UPDATE, DELETE, or DDL statements |
239
- | `transaction` | Begin, execute within, commit, or rollback transactions (PostgreSQL only) |
240
-
241
- ## Configuration
242
-
243
- ### Transport
244
-
245
- | Field | Type | Default | Description |
246
- |-------|------|---------|-------------|
247
- | `transport.type` | `"stdio"` \| `"http"` | `"stdio"` | Transport mode |
248
- | `transport.port` | number | `3000` | HTTP listen port |
249
- | `transport.host` | string | `"127.0.0.1"` | HTTP bind address |
250
- | `transport.stateless` | boolean | `false` | Disable session management |
251
- | `transport.auth.type` | `"bearer"` | — | Authentication type |
252
- | `transport.auth.token` | string | — | Bearer token value |
253
-
254
- The `transport` field is optional. When omitted, stdio is used. The `--transport` CLI flag overrides the config value.
255
-
256
- ### Defaults
257
-
258
- | Option | Type | Default | Description |
259
- |--------|------|---------|-------------|
260
- | `maxRows` | number | `100` | Maximum rows returned by `query` |
261
- | `lazyConnection` | boolean | `true` | Connect on first use instead of at startup |
262
- | `toolsPerDatabase` | boolean | `false` | Register separate tools per database (e.g. `query_main_pg`) |
263
- | `queryTimeout` | number | `30000` | Query timeout in milliseconds |
264
-
265
- ### PostgreSQL database
266
-
267
- | Field | Required | Default | Description |
268
- |-------|----------|---------|-------------|
269
- | `id` | yes | — | Unique identifier |
270
- | `type` | yes | — | Must be `"postgresql"` |
271
- | `host` | yes | — | Hostname |
272
- | `port` | no | `5432` | Port |
273
- | `database` | yes | — | Database name |
274
- | `user` | yes | — | Username |
275
- | `password` | no | `""` | Password |
276
- | `ssl` | no | — | Enable SSL |
277
- | `description` | no | — | Human-readable label |
278
- | `lazyConnection` | no | inherits | Override default |
279
- | `maxRows` | no | inherits | Override default |
280
- | `queryTimeout` | no | inherits | Override default |
281
-
282
- ### ClickHouse database
283
-
284
- | Field | Required | Default | Description |
285
- |-------|----------|---------|-------------|
286
- | `id` | yes | — | Unique identifier |
287
- | `type` | yes | — | Must be `"clickhouse"` |
288
- | `url` | yes | — | HTTP URL (e.g. `http://localhost:8123`) |
289
- | `database` | yes | — | Database name |
290
- | `user` | no | `"default"` | Username |
291
- | `password` | no | `""` | Password |
292
- | `description` | no | — | Human-readable label |
293
- | `lazyConnection` | no | inherits | Override default |
294
- | `maxRows` | no | inherits | Override default |
295
- | `queryTimeout` | no | inherits | Override default |
296
-
297
- ### Per-database tool mode
298
-
299
- Set `"toolsPerDatabase": true` in defaults to register a separate tool for each database. Instead of a single `query` tool with a `database` parameter, you get `query_main_pg`, `query_analytics`, etc. Useful when connecting many databases to avoid parameter confusion.
300
-
301
- ## Architecture
302
-
303
- ```
304
- src/
305
- ├── index.ts Entry point: CLI args → config → transport routing
306
- ├── server.ts Creates McpServer + ConnectorManager, registers tools
307
- ├── config/
308
- │ ├── types.ts Zod schemas for config validation (transport, databases)
309
- │ └── loader.ts Reads config file, parses CLI args (--config, --transport)
310
- ├── connectors/
311
- │ ├── interface.ts Connector interface and shared types
312
- │ ├── manager.ts Connector lifecycle (lazy/eager, create, disconnect)
313
- │ ├── postgresql.ts PostgreSQL implementation (pg)
314
- │ └── clickhouse.ts ClickHouse implementation (@clickhouse/client)
315
- ├── tools/
316
- │ ├── registry.ts Registers tools in parameter or per-database mode
317
- │ ├── readonly/ query, list-tables, describe-table, schema, explain, performance
318
- │ └── write/ execute, transaction
319
- ├── transport/
320
- │ └── http.ts HTTP transport: Express app, session management, auth
321
- └── utils/
322
- ├── response.ts Standardized MCP response formatting
323
- └── sql-validator.ts Blocks write keywords in read-only queries
324
- ```
104
+ | Doc | Contents |
105
+ |-----|----------|
106
+ | [docs/configuration.md](docs/configuration.md) | Every config field, defaults, SSL/TLS, `rowFormat`, env substitution, hot reload |
107
+ | [docs/tools.md](docs/tools.md) | The 9 tools: params, output, examples, error codes, per-database mode |
108
+ | [docs/http-transport.md](docs/http-transport.md) | stdio vs HTTP, sessions, bearer auth, `/health`, curl |
109
+ | [docs/security.md](docs/security.md) | Read-only SQL validator, deny-lists, response caps, network exposure |
110
+ | [docs/architecture.md](docs/architecture.md) | Source tree, layers, connector model, lifecycle (hot reload, shutdown) |
111
+ | [CONTRIBUTING.md](CONTRIBUTING.md) | Build, test, lint, hooks, CI |
325
112
 
326
113
  ## ClickHouse limitations
327
114
 
328
- - Transactions are not supported (throws an error)
329
- - Query parameters via `params` are ignored — use ClickHouse's native `{name:Type}` syntax in SQL
115
+ - Transactions are not supported (`transaction begin` throws).
116
+ - Query `params` are ignored — use ClickHouse's native `{name:Type}` syntax in the SQL.
330
117
 
331
118
  ## Development
332
119
 
333
- ```bash
334
- npm run dev -- --config config.json # Run with tsx, auto-reload
335
- npm run build # Compile TypeScript to dist/
336
- npm start -- --config config.json # Run compiled output
337
- ```
120
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for build/test/lint commands, coverage thresholds, git
121
+ hooks, and CI.
338
122
 
339
123
  ## License
340
124
 
@@ -4,6 +4,7 @@
4
4
  "port": 3000,
5
5
  "host": "127.0.0.1",
6
6
  "stateless": false,
7
+ "sessionTimeout": 1800000,
7
8
  "auth": {
8
9
  "type": "bearer",
9
10
  "token": "your-secret-token"
@@ -13,7 +14,8 @@
13
14
  "maxRows": 100,
14
15
  "lazyConnection": true,
15
16
  "toolsPerDatabase": false,
16
- "queryTimeout": 30000
17
+ "queryTimeout": 30000,
18
+ "rowFormat": "json"
17
19
  },
18
20
  "databases": [
19
21
  {
@@ -70,10 +70,14 @@ declare const DefaultsSchema: z.ZodObject<{
70
70
  toolsPerDatabase: z.ZodDefault<z.ZodBoolean>;
71
71
  queryTimeout: z.ZodDefault<z.ZodNumber>;
72
72
  logLevel: z.ZodDefault<z.ZodEnum<{
73
+ error: "error";
73
74
  debug: "debug";
74
75
  info: "info";
75
76
  warn: "warn";
76
- error: "error";
77
+ }>>;
78
+ rowFormat: z.ZodDefault<z.ZodEnum<{
79
+ json: "json";
80
+ table: "table";
77
81
  }>>;
78
82
  }, z.core.$strip>;
79
83
  declare const HttpTransportConfigSchema: z.ZodObject<{
@@ -120,23 +124,29 @@ export declare const AppConfigSchema: z.ZodObject<{
120
124
  toolsPerDatabase: z.ZodDefault<z.ZodBoolean>;
121
125
  queryTimeout: z.ZodDefault<z.ZodNumber>;
122
126
  logLevel: z.ZodDefault<z.ZodEnum<{
127
+ error: "error";
123
128
  debug: "debug";
124
129
  info: "info";
125
130
  warn: "warn";
126
- error: "error";
131
+ }>>;
132
+ rowFormat: z.ZodDefault<z.ZodEnum<{
133
+ json: "json";
134
+ table: "table";
127
135
  }>>;
128
136
  }, z.core.$strip>>, z.ZodTransform<{
129
137
  maxRows: number;
130
138
  lazyConnection: boolean;
131
139
  toolsPerDatabase: boolean;
132
140
  queryTimeout: number;
133
- logLevel: "debug" | "info" | "warn" | "error";
141
+ logLevel: "error" | "debug" | "info" | "warn";
142
+ rowFormat: "json" | "table";
134
143
  }, {
135
144
  maxRows: number;
136
145
  lazyConnection: boolean;
137
146
  toolsPerDatabase: boolean;
138
147
  queryTimeout: number;
139
- logLevel: "debug" | "info" | "warn" | "error";
148
+ logLevel: "error" | "debug" | "info" | "warn";
149
+ rowFormat: "json" | "table";
140
150
  } | undefined>>;
141
151
  databases: z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
142
152
  id: z.ZodString;
@@ -13,7 +13,7 @@ const PostgresConfigBaseSchema = z.object({
13
13
  sslCa: z.string().optional(),
14
14
  description: z.string().optional(),
15
15
  lazyConnection: z.boolean().optional(),
16
- maxRows: z.number().optional(),
16
+ maxRows: z.number().int().positive().optional(),
17
17
  queryTimeout: z.number().optional(),
18
18
  });
19
19
  const PostgresConfigSchema = PostgresConfigBaseSchema.superRefine((data, ctx) => {
@@ -39,17 +39,18 @@ const ClickHouseConfigSchema = z.object({
39
39
  .optional(),
40
40
  description: z.string().optional(),
41
41
  lazyConnection: z.boolean().optional(),
42
- maxRows: z.number().optional(),
42
+ maxRows: z.number().int().positive().optional(),
43
43
  queryTimeout: z.number().optional(),
44
44
  });
45
45
  const DatabaseConfigSchema = z.union([PostgresConfigSchema, ClickHouseConfigSchema]);
46
46
  const LogLevelSchema = z.enum(["debug", "info", "warn", "error"]);
47
47
  const DefaultsSchema = z.object({
48
- maxRows: z.number().default(100),
48
+ maxRows: z.number().int().positive().default(100),
49
49
  lazyConnection: z.boolean().default(true),
50
50
  toolsPerDatabase: z.boolean().default(false),
51
51
  queryTimeout: z.number().default(30000),
52
52
  logLevel: LogLevelSchema.default("info"),
53
+ rowFormat: z.enum(["json", "table"]).default("json"),
53
54
  });
54
55
  const HttpTransportConfigSchema = z.object({
55
56
  type: z.literal("http"),
@@ -70,13 +71,7 @@ const StdioTransportConfigSchema = z.object({
70
71
  const TransportConfigSchema = z.discriminatedUnion("type", [StdioTransportConfigSchema, HttpTransportConfigSchema]);
71
72
  export const AppConfigSchema = z.object({
72
73
  transport: TransportConfigSchema.optional().default({ type: "stdio" }),
73
- defaults: DefaultsSchema.optional().transform((v) => v ?? {
74
- maxRows: 100,
75
- lazyConnection: true,
76
- toolsPerDatabase: false,
77
- queryTimeout: 30000,
78
- logLevel: "info",
79
- }),
74
+ defaults: DefaultsSchema.optional().transform((v) => v ?? DefaultsSchema.parse({})),
80
75
  databases: z.array(DatabaseConfigSchema).min(1),
81
76
  });
82
77
  export function resolveDbConfig(db, defaults) {
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/config/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/config/types.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import type { Connector, QueryResult, TableInfo, ColumnInfo, ExplainResult, TransactionHandle } from "./interface.js";
2
1
  import type { ClickHouseConfig } from "../config/types.js";
2
+ import type { ColumnInfo, Connector, ExplainResult, QueryResult, TableInfo, TransactionHandle } from "./interface.js";
3
3
  export declare class ClickHouseConnector implements Connector {
4
4
  readonly type: "clickhouse";
5
5
  private client;
@@ -1,5 +1,6 @@
1
- import { createClient } from "@clickhouse/client";
2
1
  import { readFileSync } from "node:fs";
2
+ import { createClient } from "@clickhouse/client";
3
+ import { wrapReadonlyQuery } from "../utils/sql-validator.js";
3
4
  export class ClickHouseConnector {
4
5
  type = "clickhouse";
5
6
  client = null;
@@ -43,8 +44,15 @@ export class ClickHouseConnector {
43
44
  }
44
45
  async query(sql, _params, maxRows) {
45
46
  const limit = maxRows ?? this.maxRows;
46
- const wrappedSql = `SELECT * FROM (${sql}) AS _q LIMIT ${limit}`;
47
- const result = await this.getClient().query({ query: wrappedSql, format: "JSONEachRow" });
47
+ const wrappedSql = wrapReadonlyQuery(sql, limit, "clickhouse");
48
+ // The sql-validator deny-list is the primary guard against url()/file()/s3() SSRF; readonly:1
49
+ // is defense-in-depth (it reliably blocks writes and settings changes, not necessarily every
50
+ // read-side table function).
51
+ const result = await this.getClient().query({
52
+ query: wrappedSql,
53
+ format: "JSONEachRow",
54
+ clickhouse_settings: { readonly: "1" },
55
+ });
48
56
  const rows = (await result.json());
49
57
  return { rows, rowCount: rows.length };
50
58
  }
@@ -1 +1 @@
1
- {"version":3,"file":"clickhouse.js","sourceRoot":"","sources":["../../src/connectors/clickhouse.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"clickhouse.js","sourceRoot":"","sources":["../../src/connectors/clickhouse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAyB,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAG9D,MAAM,OAAO,mBAAmB;IACrB,IAAI,GAAG,YAAqB,CAAC;IAC9B,MAAM,GAA4B,IAAI,CAAC;IACvC,MAAM,CAAmB;IACzB,OAAO,CAAS;IAChB,YAAY,CAAS;IAE7B,YAAY,MAAwB,EAAE,YAAoB,EAAE,OAAe;QACzE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,OAAO;QACX,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE;YACnC,CAAC,CAAC;gBACE,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC;oBAClD,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;oBACjC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;gBACpC,mBAAmB,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,kBAAkB;aACxD;YACH,CAAC,CAAC,SAAS,CAAC;QAEd,IAAI,CAAC,MAAM,GAAG,YAAY,CAAC;YACzB,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG;YACpB,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;YAC9B,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI;YAC1B,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;YAC9B,eAAe,EAAE,IAAI,CAAC,YAAY;YAClC,GAAG,CAAC,SAAS,IAAI,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC;SACrC,CAAC,CAAC;QACH,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,UAAU;QACd,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YAC1B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACrB,CAAC;IACH,CAAC;IAEO,SAAS;QACf,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;QACnD,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,GAAW,EAAE,OAAkB,EAAE,OAAgB;QAC3D,MAAM,KAAK,GAAG,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC;QACtC,MAAM,UAAU,GAAG,iBAAiB,CAAC,GAAG,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC;QAC/D,8FAA8F;QAC9F,6FAA6F;QAC7F,6BAA6B;QAC7B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC;YAC1C,KAAK,EAAE,UAAU;YACjB,MAAM,EAAE,aAAa;YACrB,mBAAmB,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE;SACvC,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,CAAC,MAAM,MAAM,CAAC,IAAI,EAAE,CAA8B,CAAC;QAChE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;IACzC,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,GAAW,EAAE,OAAkB;QAC3C,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QAC/C,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,OAAgB;QAC/B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC;YAC1C,KAAK,EAAE,yFAAyF;YAChG,MAAM,EAAE,aAAa;SACtB,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,CAAC,MAAM,MAAM,CAAC,IAAI,EAAE,CAAuC,CAAC;QACzE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACtB,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;YAC5B,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;SACnD,CAAC,CAAC,CAAC;IACN,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,KAAa,EAAE,OAAgB;QACjD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC;YAC1C,KAAK,EAAE;;;gCAGmB;YAC1B,MAAM,EAAE,aAAa;YACrB,YAAY,EAAE,EAAE,KAAK,EAAE;SACxB,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,CAAC,MAAM,MAAM,CAAC,IAAI,EAAE,CAM9B,CAAC;QACJ,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACtB,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;YACvC,YAAY,EAAE,CAAC,CAAC,kBAAkB,IAAI,IAAI;YAC1C,YAAY,EAAE,CAAC,CAAC,iBAAiB,KAAK,CAAC;SACxC,CAAC,CAAC,CAAC;IACN,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,OAAgB;QAC9B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC;YAC1C,KAAK,EAAE,uFAAuF;YAC9F,MAAM,EAAE,aAAa;SACtB,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,CAAC,MAAM,MAAM,CAAC,IAAI,EAAE,CAAmD,CAAC;QACrF,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC7D,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,GAAW,EAAE,OAAO,GAAG,KAAK;QACxC,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,SAAS,CAAC;QACvD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC;YAC1C,KAAK,EAAE,GAAG,MAAM,IAAI,GAAG,EAAE;YACzB,MAAM,EAAE,aAAa;SACtB,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,CAAC,MAAM,MAAM,CAAC,IAAI,EAAE,CAA0B,CAAC;QAC5D,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;IACzD,CAAC;IAED,KAAK,CAAC,gBAAgB;QACpB,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAClE,CAAC;CACF"}
@@ -1,10 +1,11 @@
1
- import type { Connector } from "./interface.js";
2
1
  import type { ResolvedDatabaseConfig } from "../config/types.js";
3
2
  import { PerformanceTracker } from "../tools/readonly/performance.js";
3
+ import type { Connector } from "./interface.js";
4
4
  export declare class ConnectorManager {
5
5
  private configs;
6
6
  private connectors;
7
7
  private rawConnectors;
8
+ private connecting;
8
9
  private tracker;
9
10
  constructor(databases: ResolvedDatabaseConfig[]);
10
11
  getDatabaseIds(): string[];
@@ -12,7 +13,11 @@ export declare class ConnectorManager {
12
13
  getAllConfigs(): ResolvedDatabaseConfig[];
13
14
  getPerformanceTracker(): PerformanceTracker;
14
15
  getConnector(dbId: string): Promise<Connector>;
15
- withConnector<T>(dbId: string, fn: (connector: Connector) => Promise<T>): Promise<T>;
16
+ resolveId(input: string): string;
17
+ acquire(input: string): Promise<{
18
+ id: string;
19
+ connector: Connector;
20
+ }>;
16
21
  invalidateConnector(dbId: string): void;
17
22
  private createConnector;
18
23
  updateDatabases(newConfigs: ResolvedDatabaseConfig[]): {
@@ -1,25 +1,13 @@
1
- import { PostgresConnector } from "./postgresql.js";
1
+ import { PerformanceTracker } from "../tools/readonly/performance.js";
2
+ import { resolveDbId } from "../utils/resolve-db.js";
2
3
  import { ClickHouseConnector } from "./clickhouse.js";
3
4
  import { InstrumentedConnector } from "./instrumented.js";
4
- import { PerformanceTracker } from "../tools/readonly/performance.js";
5
- import { getLogger } from "../utils/logger.js";
6
- const CONNECTION_ERROR_CODES = new Set(["ECONNREFUSED", "ECONNRESET", "EPIPE", "ETIMEDOUT"]);
7
- function isConnectionError(err) {
8
- if (err instanceof Error) {
9
- const code = err.code;
10
- if (code && CONNECTION_ERROR_CODES.has(code))
11
- return true;
12
- if (err.message.includes("Connection terminated"))
13
- return true;
14
- if (err.message.includes("connection refused"))
15
- return true;
16
- }
17
- return false;
18
- }
5
+ import { PostgresConnector } from "./postgresql.js";
19
6
  export class ConnectorManager {
20
7
  configs = new Map();
21
8
  connectors = new Map();
22
9
  rawConnectors = new Map();
10
+ connecting = new Map();
23
11
  tracker = new PerformanceTracker();
24
12
  constructor(databases) {
25
13
  for (const db of databases) {
@@ -42,32 +30,47 @@ export class ConnectorManager {
42
30
  const existing = this.connectors.get(dbId);
43
31
  if (existing)
44
32
  return existing;
33
+ const inFlight = this.connecting.get(dbId);
34
+ if (inFlight)
35
+ return inFlight;
45
36
  const config = this.configs.get(dbId);
46
37
  if (!config)
47
38
  throw new Error(`Unknown database: ${dbId}`);
48
- const raw = this.createConnector(config);
49
- await raw.connect();
50
- this.rawConnectors.set(dbId, raw);
51
- const instrumented = new InstrumentedConnector(raw, this.tracker, dbId);
52
- this.connectors.set(dbId, instrumented);
53
- return instrumented;
54
- }
55
- async withConnector(dbId, fn) {
56
- const connector = await this.getConnector(dbId);
39
+ // Store the connect promise before awaiting so concurrent callers hitting
40
+ // the same miss join this one instead of each creating their own pool.
41
+ const connectPromise = (async () => {
42
+ const raw = this.createConnector(config);
43
+ await raw.connect();
44
+ // A concurrent updateDatabases()/invalidateConnector() may have replaced or removed this
45
+ // db's config while we were connecting. Storing the connector now would pin the manager to
46
+ // superseded (e.g. rotated/revoked) credentials, so discard it and let the caller retry.
47
+ if (this.configs.get(dbId) !== config) {
48
+ await raw.disconnect().catch(() => { });
49
+ throw new Error(`Database "${dbId}" was reconfigured during connection; please retry`);
50
+ }
51
+ this.rawConnectors.set(dbId, raw);
52
+ const instrumented = new InstrumentedConnector(raw, this.tracker, dbId);
53
+ this.connectors.set(dbId, instrumented);
54
+ return instrumented;
55
+ })();
56
+ this.connecting.set(dbId, connectPromise);
57
57
  try {
58
- return await fn(connector);
58
+ return await connectPromise;
59
59
  }
60
- catch (err) {
61
- if (isConnectionError(err)) {
62
- const logger = getLogger();
63
- logger.warn("Connection error, retrying", { database: dbId, error: String(err) });
64
- this.invalidateConnector(dbId);
65
- const retryConnector = await this.getConnector(dbId);
66
- return await fn(retryConnector);
67
- }
68
- throw err;
60
+ finally {
61
+ this.connecting.delete(dbId);
69
62
  }
70
63
  }
64
+ // Single source of truth for fuzzy id resolution. Handlers must go through this (or acquire())
65
+ // rather than calling resolveDbId directly, so id-resolution stays consistent everywhere.
66
+ resolveId(input) {
67
+ return resolveDbId(this.getDatabaseIds(), input);
68
+ }
69
+ async acquire(input) {
70
+ const id = this.resolveId(input);
71
+ const connector = await this.getConnector(id);
72
+ return { id, connector };
73
+ }
71
74
  invalidateConnector(dbId) {
72
75
  const raw = this.rawConnectors.get(dbId);
73
76
  if (raw) {