@tejasanik/postgres-mcp-server 1.4.0 → 1.6.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/dist/index.js CHANGED
@@ -1,403 +1,295 @@
1
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 { resetDbManager } from './db-manager.js';
6
- import { listServersAndDbs, switchServerDb, getCurrentConnection, listSchemas, listObjects, getObjectDetails, executeSql, explainQuery, getTopQueries, analyzeWorkloadIndexes, analyzeQueryIndexes, analyzeDbHealth } from './tools/index.js';
7
- // Tool definitions
8
- const tools = [
9
- {
10
- name: 'list_servers_and_dbs',
11
- description: 'Lists all configured PostgreSQL servers and their databases. Use fetchDatabases=true to list databases. Use searchAllServers=true to fetch databases from all servers (not just the connected one).',
12
- inputSchema: {
13
- type: 'object',
14
- properties: {
15
- serverFilter: {
16
- type: 'string',
17
- description: 'Filter servers by name or host (case-insensitive partial match)'
18
- },
19
- databaseFilter: {
20
- type: 'string',
21
- description: 'Filter databases by name (case-insensitive partial match)'
22
- },
23
- includeSystemDbs: {
24
- type: 'boolean',
25
- description: 'Include system databases (template0, template1)',
26
- default: false
27
- },
28
- fetchDatabases: {
29
- type: 'boolean',
30
- description: 'Fetch list of databases from servers',
31
- default: false
32
- },
33
- searchAllServers: {
34
- type: 'boolean',
35
- description: 'When true, fetch databases from all configured servers (temporarily connects to each). When false, only fetch from currently connected server.',
36
- default: false
37
- }
38
- }
39
- }
40
- },
41
- {
42
- name: 'switch_server_db',
43
- description: 'Switch to a different PostgreSQL server and optionally a specific database and schema. Must be called before using other database tools. Uses server defaults if not specified.',
44
- inputSchema: {
45
- type: 'object',
46
- properties: {
47
- server: {
48
- type: 'string',
49
- description: 'Name of the server to connect to (from POSTGRES_SERVERS config)'
50
- },
51
- database: {
52
- type: 'string',
53
- description: 'Name of the database to connect to (uses server default or "postgres")'
54
- },
55
- schema: {
56
- type: 'string',
57
- description: 'Name of the default schema (uses server default or "public")'
58
- }
59
- },
60
- required: ['server']
61
- }
62
- },
63
- {
64
- name: 'get_current_connection',
65
- description: 'Returns details about the current database connection including server, database, schema, host, port, and access mode (readonly/full).',
66
- inputSchema: {
67
- type: 'object',
68
- properties: {}
69
- }
70
- },
71
- {
72
- name: 'list_schemas',
73
- description: 'Lists all database schemas available in the current PostgreSQL database.',
74
- inputSchema: {
75
- type: 'object',
76
- properties: {
77
- includeSystemSchemas: {
78
- type: 'boolean',
79
- description: 'Include system schemas (pg_catalog, information_schema, etc.)',
80
- default: false
81
- }
82
- }
83
- }
84
- },
85
- {
86
- name: 'list_objects',
87
- description: 'Lists database objects (tables, views, sequences, extensions) within a specified schema.',
88
- inputSchema: {
89
- type: 'object',
90
- properties: {
91
- schema: {
92
- type: 'string',
93
- description: 'Schema name to list objects from'
94
- },
95
- objectType: {
96
- type: 'string',
97
- enum: ['table', 'view', 'sequence', 'extension', 'all'],
98
- description: 'Type of objects to list',
99
- default: 'all'
100
- },
101
- filter: {
102
- type: 'string',
103
- description: 'Filter objects by name (case-insensitive partial match)'
104
- }
105
- },
106
- required: ['schema']
107
- }
108
- },
109
- {
110
- name: 'get_object_details',
111
- description: 'Provides detailed information about a specific database object including columns, constraints, indexes, size, and row count.',
112
- inputSchema: {
113
- type: 'object',
114
- properties: {
115
- schema: {
116
- type: 'string',
117
- description: 'Schema name containing the object'
118
- },
119
- objectName: {
120
- type: 'string',
121
- description: 'Name of the object to get details for'
122
- },
123
- objectType: {
124
- type: 'string',
125
- enum: ['table', 'view', 'sequence'],
126
- description: 'Type of the object'
127
- }
128
- },
129
- required: ['schema', 'objectName']
130
- }
131
- },
132
- {
133
- name: 'execute_sql',
134
- description: 'Executes SQL statements on the database. Read-only mode prevents write operations (INSERT, UPDATE, DELETE, etc.). Large outputs are written to a temp file and the file path is returned.',
135
- inputSchema: {
136
- type: 'object',
137
- properties: {
138
- sql: {
139
- type: 'string',
140
- description: 'SQL statement to execute'
141
- },
142
- maxRows: {
143
- type: 'number',
144
- description: 'Maximum rows to return directly (default: 1000). Larger results are written to file.',
145
- default: 1000
146
- }
147
- },
148
- required: ['sql']
149
- }
150
- },
151
- {
152
- name: 'explain_query',
153
- description: 'Gets the execution plan for a SQL query, showing how PostgreSQL will process it. Can simulate hypothetical indexes if hypopg extension is installed.',
154
- inputSchema: {
155
- type: 'object',
156
- properties: {
157
- sql: {
158
- type: 'string',
159
- description: 'SQL query to explain'
160
- },
161
- analyze: {
162
- type: 'boolean',
163
- description: 'Actually execute the query to get real timing (default: false). Only allowed for SELECT queries.',
164
- default: false
165
- },
166
- buffers: {
167
- type: 'boolean',
168
- description: 'Include buffer usage statistics',
169
- default: false
170
- },
171
- format: {
172
- type: 'string',
173
- enum: ['text', 'json', 'yaml', 'xml'],
174
- description: 'Output format for the plan',
175
- default: 'json'
176
- },
177
- hypotheticalIndexes: {
178
- type: 'array',
179
- description: 'Hypothetical indexes to simulate (requires hypopg extension)',
180
- items: {
181
- type: 'object',
182
- properties: {
183
- table: {
184
- type: 'string',
185
- description: 'Table name for the hypothetical index'
186
- },
187
- columns: {
188
- type: 'array',
189
- items: { type: 'string' },
190
- description: 'Columns to include in the index'
191
- },
192
- indexType: {
193
- type: 'string',
194
- description: 'Index type (btree, hash, gist, etc.)',
195
- default: 'btree'
196
- }
197
- },
198
- required: ['table', 'columns']
199
- }
200
- }
201
- },
202
- required: ['sql']
203
- }
204
- },
205
- {
206
- name: 'get_top_queries',
207
- description: 'Reports the slowest SQL queries based on total execution time using pg_stat_statements data. Requires pg_stat_statements extension.',
208
- inputSchema: {
209
- type: 'object',
210
- properties: {
211
- limit: {
212
- type: 'number',
213
- description: 'Number of queries to return (1-100)',
214
- default: 10
215
- },
216
- orderBy: {
217
- type: 'string',
218
- enum: ['total_time', 'mean_time', 'calls'],
219
- description: 'How to order the results',
220
- default: 'total_time'
221
- },
222
- minCalls: {
223
- type: 'number',
224
- description: 'Minimum number of calls to include a query',
225
- default: 1
226
- }
227
- }
228
- }
229
- },
230
- {
231
- name: 'analyze_workload_indexes',
232
- description: 'Analyzes the database workload (using pg_stat_statements) to identify resource-intensive queries and recommends optimal indexes for them.',
233
- inputSchema: {
234
- type: 'object',
235
- properties: {
236
- topQueriesCount: {
237
- type: 'number',
238
- description: 'Number of top queries to analyze (1-50)',
239
- default: 20
240
- },
241
- includeHypothetical: {
242
- type: 'boolean',
243
- description: 'Include hypothetical index analysis (requires hypopg)',
244
- default: false
245
- }
246
- }
247
- }
248
- },
249
- {
250
- name: 'analyze_query_indexes',
251
- description: 'Analyzes specific SQL queries (up to 10) and recommends optimal indexes for them.',
252
- inputSchema: {
253
- type: 'object',
254
- properties: {
255
- queries: {
256
- type: 'array',
257
- items: { type: 'string' },
258
- description: 'List of SQL queries to analyze (max 10)',
259
- maxItems: 10
260
- }
261
- },
262
- required: ['queries']
263
- }
264
- },
265
- {
266
- name: 'analyze_db_health',
267
- description: 'Performs comprehensive database health checks including: buffer cache hit rates, connection health, constraint validation, index health (duplicate/unused/invalid), sequence limits, and vacuum health.',
268
- inputSchema: {
269
- type: 'object',
270
- properties: {}
271
- }
272
- }
273
- ];
274
- // Create MCP server
275
- const server = new Server({
276
- name: 'postgres-mcp-server',
277
- version: '1.0.0',
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { z } from "zod";
5
+ import { resetDbManager } from "./db-manager.js";
6
+ import { listServers, listDatabases, switchServerDb, getCurrentConnection, listSchemas, listObjects, getObjectDetails, executeSql, explainQuery, getTopQueries, analyzeWorkloadIndexes, analyzeQueryIndexes, analyzeDbHealth, } from "./tools/index.js";
7
+ // Create MCP server using the new high-level API
8
+ const server = new McpServer({
9
+ name: "postgres-mcp-server",
10
+ version: "1.6.0",
278
11
  }, {
279
12
  capabilities: {
280
13
  tools: {},
281
14
  },
282
15
  });
283
- // Handle list_tools request
284
- server.setRequestHandler(ListToolsRequestSchema, async () => {
285
- return { tools };
16
+ // Register tools with improved descriptions
17
+ server.registerTool("list_servers", {
18
+ description: "List all configured PostgreSQL servers. Call this FIRST to discover available server names before using list_databases or switch_server_db. Returns server names, hosts, ports, and connection status.",
19
+ inputSchema: z.object({
20
+ filter: z
21
+ .string()
22
+ .optional()
23
+ .describe("Filter servers by name or host (case-insensitive partial match)"),
24
+ }),
25
+ }, async (args) => {
26
+ const result = await listServers(args);
27
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
286
28
  });
287
- // Handle tool calls
288
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
289
- const { name, arguments: args } = request.params;
290
- try {
291
- let result;
292
- switch (name) {
293
- case 'list_servers_and_dbs':
294
- result = await listServersAndDbs(args);
295
- break;
296
- case 'switch_server_db':
297
- result = await switchServerDb(args);
298
- break;
299
- case 'get_current_connection':
300
- result = await getCurrentConnection();
301
- break;
302
- case 'list_schemas':
303
- result = await listSchemas(args);
304
- break;
305
- case 'list_objects':
306
- result = await listObjects(args);
307
- break;
308
- case 'get_object_details':
309
- result = await getObjectDetails(args);
310
- break;
311
- case 'execute_sql':
312
- result = await executeSql(args);
313
- // Special handling for large output
314
- if (result.outputFile) {
315
- return {
316
- content: [
317
- {
318
- type: 'text',
319
- text: JSON.stringify({
320
- message: `Output too large (${result.rowCount} rows). Results written to file.`,
321
- outputFile: result.outputFile,
322
- rowCount: result.rowCount,
323
- fields: result.fields,
324
- hint: 'Read the file optimally using offset/limit or run the query with filters to reduce output.'
325
- }, null, 2)
326
- }
327
- ]
328
- };
329
- }
330
- break;
331
- case 'explain_query':
332
- result = await explainQuery(args);
333
- break;
334
- case 'get_top_queries':
335
- result = await getTopQueries(args);
336
- break;
337
- case 'analyze_workload_indexes':
338
- result = await analyzeWorkloadIndexes(args);
339
- break;
340
- case 'analyze_query_indexes':
341
- result = await analyzeQueryIndexes(args);
342
- break;
343
- case 'analyze_db_health':
344
- result = await analyzeDbHealth();
345
- break;
346
- default:
347
- throw new Error(`Unknown tool: ${name}`);
348
- }
349
- return {
350
- content: [
351
- {
352
- type: 'text',
353
- text: JSON.stringify(result, null, 2)
354
- }
355
- ]
356
- };
357
- }
358
- catch (error) {
359
- const errorMessage = error instanceof Error ? error.message : String(error);
29
+ server.registerTool("list_databases", {
30
+ description: "List databases in a specific PostgreSQL server. REQUIRES serverName parameter - use list_servers first to get valid server names. Do NOT guess server names.",
31
+ inputSchema: z.object({
32
+ serverName: z
33
+ .string()
34
+ .describe("REQUIRED: Server name from list_servers. Do NOT use database names here."),
35
+ filter: z
36
+ .string()
37
+ .optional()
38
+ .describe("Filter databases by name (case-insensitive partial match)"),
39
+ includeSystemDbs: z
40
+ .boolean()
41
+ .optional()
42
+ .default(false)
43
+ .describe("Include system databases (template0, template1)"),
44
+ maxResults: z
45
+ .number()
46
+ .optional()
47
+ .default(50)
48
+ .describe("Maximum databases to return (default: 50, max: 200)"),
49
+ }),
50
+ }, async (args) => {
51
+ const result = await listDatabases(args);
52
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
53
+ });
54
+ server.registerTool("switch_server_db", {
55
+ description: "Connect to a PostgreSQL server and database. MUST be called before executing queries. Use list_servers to find server names, list_databases to find database names.",
56
+ inputSchema: z.object({
57
+ server: z.string().describe("Server name from list_servers (NOT the host)"),
58
+ database: z
59
+ .string()
60
+ .optional()
61
+ .describe("Database name from list_databases (defaults to server's default or 'postgres')"),
62
+ schema: z
63
+ .string()
64
+ .optional()
65
+ .describe("Schema name (defaults to 'public')"),
66
+ }),
67
+ }, async (args) => {
68
+ const result = await switchServerDb(args);
69
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
70
+ });
71
+ server.registerTool("get_current_connection", {
72
+ description: "Get current connection status. Returns server, database, schema, host, port, and access mode (readonly/full). Call this to verify your connection before running queries.",
73
+ inputSchema: z.object({}),
74
+ }, async () => {
75
+ const result = await getCurrentConnection();
76
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
77
+ });
78
+ server.registerTool("list_schemas", {
79
+ description: "List all schemas in the current database. Requires active connection (use switch_server_db first).",
80
+ inputSchema: z.object({
81
+ includeSystemSchemas: z
82
+ .boolean()
83
+ .optional()
84
+ .default(false)
85
+ .describe("Include system schemas (pg_catalog, information_schema, etc.)"),
86
+ }),
87
+ }, async (args) => {
88
+ const result = await listSchemas(args);
89
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
90
+ });
91
+ server.registerTool("list_objects", {
92
+ description: "List tables, views, sequences, or extensions in a schema. Requires active connection.",
93
+ inputSchema: z.object({
94
+ schema: z.string().describe("Schema name (e.g., 'public')"),
95
+ objectType: z
96
+ .enum(["table", "view", "sequence", "extension", "all"])
97
+ .optional()
98
+ .default("all")
99
+ .describe("Type of objects to list"),
100
+ filter: z
101
+ .string()
102
+ .optional()
103
+ .describe("Filter objects by name (case-insensitive partial match)"),
104
+ }),
105
+ }, async (args) => {
106
+ const result = await listObjects(args);
107
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
108
+ });
109
+ server.registerTool("get_object_details", {
110
+ description: "Get detailed info about a table/view/sequence: columns, data types, constraints, indexes, size, row count. Use this to understand table structure before writing queries.",
111
+ inputSchema: z.object({
112
+ schema: z.string().describe("Schema name containing the object"),
113
+ objectName: z.string().describe("Name of the table, view, or sequence"),
114
+ objectType: z
115
+ .enum(["table", "view", "sequence"])
116
+ .optional()
117
+ .describe("Object type (auto-detected if not specified)"),
118
+ }),
119
+ }, async (args) => {
120
+ const result = await getObjectDetails(args);
121
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
122
+ });
123
+ server.registerTool("execute_sql", {
124
+ description: "Execute SQL queries. Supports SELECT, INSERT, UPDATE, DELETE (if not in readonly mode). Use $1, $2 placeholders with params array to prevent SQL injection. Returns rows, execution time, and pagination info.",
125
+ inputSchema: z.object({
126
+ sql: z
127
+ .string()
128
+ .describe("SQL statement. Use $1, $2, etc. for parameterized queries."),
129
+ params: z
130
+ .array(z.any())
131
+ .optional()
132
+ .describe("Parameters for $1, $2, etc. placeholders (e.g., [123, 'value'])"),
133
+ maxRows: z
134
+ .number()
135
+ .optional()
136
+ .default(1000)
137
+ .describe("Max rows to return (default: 1000, max: 100000)"),
138
+ offset: z
139
+ .number()
140
+ .optional()
141
+ .default(0)
142
+ .describe("Skip rows for pagination"),
143
+ allowLargeScript: z
144
+ .boolean()
145
+ .optional()
146
+ .default(false)
147
+ .describe("Bypass 100KB SQL limit for deployment scripts"),
148
+ }),
149
+ }, async (args) => {
150
+ const result = await executeSql(args);
151
+ // Special handling for large output
152
+ if (result.outputFile) {
360
153
  return {
361
154
  content: [
362
155
  {
363
- type: 'text',
364
- text: JSON.stringify({ error: errorMessage }, null, 2)
365
- }
156
+ type: "text",
157
+ text: JSON.stringify({
158
+ message: `Output too large (${result.rowCount} rows). Results written to file.`,
159
+ outputFile: result.outputFile,
160
+ rowCount: result.rowCount,
161
+ fields: result.fields,
162
+ hint: "Use offset/maxRows to paginate, or add WHERE clauses to reduce results.",
163
+ }, null, 2),
164
+ },
366
165
  ],
367
- isError: true
368
166
  };
369
167
  }
168
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
169
+ });
170
+ server.registerTool("explain_query", {
171
+ description: "Show PostgreSQL's execution plan for a query. Use this to understand query performance and identify missing indexes. analyze=true runs the query to get actual timings (SELECT only).",
172
+ inputSchema: z.object({
173
+ sql: z.string().describe("SQL query to explain"),
174
+ analyze: z
175
+ .boolean()
176
+ .optional()
177
+ .default(false)
178
+ .describe("Execute query for real timing (SELECT only, blocked for writes)"),
179
+ buffers: z
180
+ .boolean()
181
+ .optional()
182
+ .default(false)
183
+ .describe("Include buffer/cache statistics"),
184
+ format: z
185
+ .enum(["text", "json", "yaml", "xml"])
186
+ .optional()
187
+ .default("json")
188
+ .describe("Output format"),
189
+ hypotheticalIndexes: z
190
+ .array(z.object({
191
+ table: z.string().describe("Table name"),
192
+ columns: z.array(z.string()).describe("Columns for the index"),
193
+ indexType: z.string().optional().default("btree").describe("Index type"),
194
+ }))
195
+ .optional()
196
+ .describe("Test hypothetical indexes (requires hypopg extension)"),
197
+ }),
198
+ }, async (args) => {
199
+ const result = await explainQuery(args);
200
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
201
+ });
202
+ server.registerTool("get_top_queries", {
203
+ description: "Find slowest queries from pg_stat_statements. Requires pg_stat_statements extension enabled. Use this to identify performance bottlenecks.",
204
+ inputSchema: z.object({
205
+ limit: z
206
+ .number()
207
+ .optional()
208
+ .default(10)
209
+ .describe("Number of queries to return (1-100)"),
210
+ orderBy: z
211
+ .enum(["total_time", "mean_time", "calls"])
212
+ .optional()
213
+ .default("total_time")
214
+ .describe("Sort by total time, average time, or call count"),
215
+ minCalls: z
216
+ .number()
217
+ .optional()
218
+ .default(1)
219
+ .describe("Minimum call count to include"),
220
+ }),
221
+ }, async (args) => {
222
+ const result = await getTopQueries(args);
223
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
224
+ });
225
+ server.registerTool("analyze_workload_indexes", {
226
+ description: "Analyze database workload and recommend indexes. Uses pg_stat_statements to find slow queries and suggests indexes to improve them.",
227
+ inputSchema: z.object({
228
+ topQueriesCount: z
229
+ .number()
230
+ .optional()
231
+ .default(20)
232
+ .describe("Number of top queries to analyze (1-50)"),
233
+ includeHypothetical: z
234
+ .boolean()
235
+ .optional()
236
+ .default(false)
237
+ .describe("Test recommendations with hypothetical indexes (requires hypopg)"),
238
+ }),
239
+ }, async (args) => {
240
+ const result = await analyzeWorkloadIndexes(args);
241
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
242
+ });
243
+ server.registerTool("analyze_query_indexes", {
244
+ description: "Recommend indexes for specific SQL queries. Provide up to 10 SELECT queries and get index recommendations.",
245
+ inputSchema: z.object({
246
+ queries: z
247
+ .array(z.string())
248
+ .max(10)
249
+ .describe("SQL SELECT queries to analyze (max 10)"),
250
+ }),
251
+ }, async (args) => {
252
+ const result = await analyzeQueryIndexes(args);
253
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
254
+ });
255
+ server.registerTool("analyze_db_health", {
256
+ description: "Run comprehensive database health checks: cache hit rates, connection usage, index health (invalid/unused/duplicate), vacuum status, sequence limits, unvalidated constraints. Returns issues with severity levels.",
257
+ inputSchema: z.object({}),
258
+ }, async () => {
259
+ const result = await analyzeDbHealth();
260
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
370
261
  });
371
262
  // Graceful shutdown handling
372
263
  async function shutdown() {
373
- console.error('Shutting down PostgreSQL MCP Server...');
264
+ console.error("Shutting down PostgreSQL MCP Server...");
374
265
  try {
375
266
  resetDbManager();
267
+ await server.close();
376
268
  }
377
269
  catch (error) {
378
- console.error('Error during shutdown:', error);
270
+ console.error("Error during shutdown:", error);
379
271
  }
380
272
  process.exit(0);
381
273
  }
382
- process.on('SIGINT', shutdown);
383
- process.on('SIGTERM', shutdown);
384
- process.on('SIGHUP', shutdown);
274
+ process.on("SIGINT", shutdown);
275
+ process.on("SIGTERM", shutdown);
276
+ process.on("SIGHUP", shutdown);
385
277
  // Handle uncaught errors
386
- process.on('uncaughtException', (error) => {
387
- console.error('Uncaught exception:', error);
278
+ process.on("uncaughtException", (error) => {
279
+ console.error("Uncaught exception:", error);
388
280
  shutdown();
389
281
  });
390
- process.on('unhandledRejection', (reason, promise) => {
391
- console.error('Unhandled rejection at:', promise, 'reason:', reason);
282
+ process.on("unhandledRejection", (reason, promise) => {
283
+ console.error("Unhandled rejection at:", promise, "reason:", reason);
392
284
  });
393
285
  // Start server
394
286
  async function main() {
395
287
  const transport = new StdioServerTransport();
396
288
  await server.connect(transport);
397
- console.error('PostgreSQL MCP Server started');
289
+ console.error("PostgreSQL MCP Server started");
398
290
  }
399
291
  main().catch((error) => {
400
- console.error('Fatal error:', error);
292
+ console.error("Fatal error:", error);
401
293
  process.exit(1);
402
294
  });
403
295
  //# sourceMappingURL=index.js.map