execute_query
Run SQL queries on Turso databases directly from LLMs using the MCP server. Input the query, optional database name, and parameters to retrieve or modify data efficiently.
Instructions
Executes a SQL query against a database
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| database | No | Database name (optional, uses context if not provided) | |
| params | No | Query parameters (optional) | |
| query | Yes | SQL query to execute |
Implementation Reference
- src/clients/database.ts:120-151 (handler)Core implementation that executes the SQL query against the Turso database client, handling read-only detection, permission selection, parameter conversion, and error wrapping.export async function execute_query( database_name: string, query: string, params: Record<string, any> = {}, ): Promise<ResultSet> { try { // Determine if this is a read-only query const is_read_only = query .trim() .toLowerCase() .startsWith('select'); const permission = is_read_only ? 'read-only' : 'full-access'; const client = await get_database_client( database_name, permission, ); // Execute the query return await client.execute({ sql: query, args: convert_parameters(params), }); } catch (error) { throw new TursoApiError( `Failed to execute query for database ${database_name}: ${ (error as Error).message }`, 500, ); } }
- src/tools/handler.ts:243-281 (registration)MCP tool registration for 'execute_query', including input schema reference, safety validation to prevent read-only misuse, database context resolution, delegation to core handler, result formatting, and error handling.server.tool( { name: 'execute_query', description: `⚠️ DESTRUCTIVE: Execute SQL that can modify/delete data (INSERT, UPDATE, DELETE, DROP, ALTER). Always confirm with user before destructive operations.`, schema: QuerySchema, }, async ({ query, params = {}, database }) => { try { // Validate that this is not a read-only query const normalized_query = query.trim().toLowerCase(); if ( normalized_query.startsWith('select') || normalized_query.startsWith('pragma') ) { throw new Error( 'SELECT and PRAGMA queries should use execute_read_only_query', ); } const database_name = resolve_database_name(database); if (database) set_current_database(database); const result = await database_client.execute_query( database_name, query, params, ); const formatted_result = format_query_result(result); return create_tool_response({ database: database_name, query, result: formatted_result, }); } catch (error) { return create_tool_error_response(error); } }, );
- src/tools/handler.ts:36-40 (schema)Zod input schema validation for the execute_query tool parameters.const QuerySchema = z.object({ query: z.string().describe('SQL query to execute'), params: z.record(z.string(), z.any()).optional().describe('Query parameters (optional) - Use parameterized queries for security'), database: z.string().optional().describe('Database name (optional, uses context if not provided)'), });
- src/clients/database.ts:12-36 (helper)Utility to convert input parameters to libsql client expected format (named object or positional array).function convert_parameters(params: Record<string, any>): any { if (!params || Object.keys(params).length === 0) { return {}; } // Check if parameters are positional (numbered keys like "1", "2", etc.) const keys = Object.keys(params); const is_positional = keys.every((key) => /^\d+$/.test(key)); if (is_positional) { // Convert to array for positional parameters const max_index = Math.max(...keys.map((k) => parseInt(k))); const param_array: any[] = new Array(max_index); for (const [key, value] of Object.entries(params)) { const index = parseInt(key) - 1; // Convert 1-based to 0-based indexing param_array[index] = value; } return param_array; } // Return as-is for named parameters return params; }
- src/tools/handler.ts:366-380 (helper)Formats the libsql ResultSet for JSON-safe tool response, converting BigInt to string.function format_query_result(result: ResultSet): any { // Convert BigInt to string to avoid serialization issues const lastInsertRowid = result.lastInsertRowid !== null && typeof result.lastInsertRowid === 'bigint' ? result.lastInsertRowid.toString() : result.lastInsertRowid; return { rows: result.rows, rowsAffected: result.rowsAffected, lastInsertRowid: lastInsertRowid, columns: result.columns, }; }