feat: add execute_sql tool
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
||||
import * as z from 'zod'
|
||||
import type { Manager } from '../db/manager.js'
|
||||
import { clampRowLimit, formatDbError, MAX_ROW_LIMIT } from '../format.js'
|
||||
import { errorMessage, fail, ok } from './respond.js'
|
||||
|
||||
export const registerQueryTools = (server: McpServer, manager: Manager): void => {
|
||||
server.registerTool(
|
||||
'execute_sql',
|
||||
{
|
||||
description:
|
||||
'Execute a single SQL statement on a named connection. SELECT returns columns/rows; ' +
|
||||
'DML returns rowCount (and lastInsertId for mysql). Use positional params: $1.. for postgres, ? for mysql.',
|
||||
inputSchema: {
|
||||
connection: z.string().describe('connection name, see list_connections'),
|
||||
sql: z.string().min(1),
|
||||
params: z
|
||||
.array(z.union([z.string(), z.number(), z.boolean(), z.null()]))
|
||||
.optional()
|
||||
.describe('positional query parameters'),
|
||||
database: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe('database to run against; defaults to the connection default'),
|
||||
rowLimit: z.number().int().min(1).max(MAX_ROW_LIMIT).optional()
|
||||
}
|
||||
},
|
||||
async ({ connection, sql, params, database, rowLimit }) => {
|
||||
let managed: Awaited<ReturnType<Manager['get']>>
|
||||
try {
|
||||
managed = await manager.get(connection)
|
||||
} catch (error) {
|
||||
return fail(errorMessage(error))
|
||||
}
|
||||
try {
|
||||
const result = await managed.driver.query({
|
||||
sql,
|
||||
params: params ?? [],
|
||||
database,
|
||||
rowLimit: clampRowLimit(rowLimit)
|
||||
})
|
||||
return ok(result)
|
||||
} catch (error) {
|
||||
return fail(formatDbError(managed.config.type, error))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user