UNPKG

clickhouse-mcp

Version:
148 lines 4.62 kB
import { createClient } from "@clickhouse/client"; import { getClientConfig } from "../config.js"; import { Logger } from "../utils/logger.js"; const logger = new Logger("clickhouse-client"); let clientInstance = null; /** * Create or return an existing ClickHouse client. * * @returns A configured ClickHouse client */ export function getClickHouseClient() { if (clientInstance) { return clientInstance; } const clientConfig = getClientConfig(); logger.debug(`Client config: ${JSON.stringify(clientConfig)}`); logger.info(`Creating ClickHouse client connection to ${clientConfig.host}:${clientConfig.port} ` + `as ${clientConfig.username} ` + `(secure=${clientConfig.secure}, verify=${clientConfig.verify}, ` + `connect_timeout=${clientConfig.connect_timeout}s, ` + `send_receive_timeout=${clientConfig.send_receive_timeout}s)`); try { clientInstance = createClient({ host: `${clientConfig.secure ? "https" : "http"}://${clientConfig.host}:${clientConfig.port}`, username: clientConfig.username, password: clientConfig.password, database: clientConfig.database, request_timeout: clientConfig.send_receive_timeout * 1000, // Convert to ms // connect_timeout: clientConfig.connect_timeout * 1000, // Convert to ms }); // clientInstance = createClient({ // host: "http://localhost:8123", // username: "default", // password: "clickhouse", // request_timeout: 30000, // }); return clientInstance; } catch (error) { logger.error(`Failed to create ClickHouse client: ${error}`); throw error; } } /** * Execute a SELECT query with timeout. * * @param query SQL query string * @param timeoutMs Timeout in milliseconds * @returns Query results */ export async function executeSelectQuery(query, timeoutMs = 30000) { const client = getClickHouseClient(); logger.info(`Executing SELECT query with ${timeoutMs}ms timeout: ${query}`); try { // Create the query promise const result = await client.query({ query, format: "JSONEachRow", }); // Process the result properly const json = await result.json(); logger.info(`Query returned results successfully`); return json; } catch (error) { const message = error instanceof Error ? error.message : String(error); logger.error(`Error executing query: ${message}`); // Return a structured response for errors return { status: "error", message: `Query failed: ${message}`, }; } } /** * Execute a command query (non-SELECT). * * @param query SQL command to execute * @returns Success status */ export async function executeCommand(query) { const client = getClickHouseClient(); logger.info(`Executing command: ${query}`); try { await client.command({ query, }); return { success: true }; } catch (error) { const message = error instanceof Error ? error.message : String(error); logger.error(`Error executing command: ${message}`); return { success: false, message: message, }; } } /** * Quote an identifier for safe use in SQL. * * @param identifier SQL identifier to quote * @returns Quoted identifier */ export function quoteIdentifier(identifier) { return `\`${identifier.replace(/`/g, "\\`")}\``; } /** * Format a value for use in a SQL query. * * @param value Value to format * @returns Formatted value for SQL */ export function formatQueryValue(value) { if (value === null || value === undefined) { return "NULL"; } if (typeof value === "string") { return `'${value.replace(/'/g, "\\'")}'`; } if (typeof value === "boolean") { return value ? "1" : "0"; } return String(value); } /** * Get the ClickHouse server version. * * @returns Server version string */ export async function getServerVersion() { const client = getClickHouseClient(); try { const result = await client.query({ query: "SELECT version() AS version", format: "JSONEachRow", }); if (result.rows.length > 0) { return result.rows[0].version; } return "unknown"; } catch (error) { logger.error(`Error getting server version: ${error}`); return "unknown"; } } //# sourceMappingURL=client.js.map