// postgres's tools — postgres's own code (novox/hq ADR 0039), importing postgres's own client. They // return structured data; the mesh serves them through the sdk's tool harness. Both call through // PostgresClient.query(), the module's one pending execution boundary (see client.ts): the tool // shapes are fixed and correct, and surface the TODO honestly until that boundary is backed. import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools"; import { PostgresClient } from "../client.js"; export function getPostgresTools(postgres: PostgresClient): ToolDefinition[] { return [ { name: "postgres_list_databases", description: "List the databases on the postgres server, with their on-disk size.", input: {}, run: async () => ({ databases: await postgres.listDatabases() }), }, { name: "postgres_query", description: "Run a read-only SQL query against a named database, as a login that can read every table and change nothing.", input: { database: { type: "string", description: "the database to query" }, sql: { type: "string", description: "the SELECT (or other read-only) statement" }, }, run: async (args) => { const database = String(args.database ?? ""); const sql = String(args.sql ?? ""); if (!database) throw new Error("postgres_query: database is required"); if (!sql) throw new Error("postgres_query: sql is required"); const result = await postgres.readOnlyQuery(database, sql); return { database, command: result.command, rows: result.rows }; }, }, ]; } // The store seat's verbs (novox/hq ADR 0159, 0160): the role's, not postgres's. Registered under the // seat's name, so the runtime serves them on the seat's subjects wherever this module holds the // seat and never lists them as postgres's own; scoped to what the store enables — asking what it // holds and reading from it — so creating a database is postgres's tool and not the store's. export function getStoreVerbs(postgres: PostgresClient): ToolDefinition[] { return [ { name: "databases", description: "Every database the store holds, with its on-disk size.", input: {}, run: async () => ({ databases: await postgres.listDatabases() }), }, { name: "query", description: "One read-only statement against one database the store holds.", input: { database: { type: "string", description: "the database to query" }, sql: { type: "string", description: "the SELECT (or other read-only) statement" }, }, run: async (args) => { const database = String(args.database ?? ""); const sql = String(args.sql ?? ""); if (!database) throw new Error("query: database is required"); if (!sql) throw new Error("query: sql is required"); const result = await postgres.readOnlyQuery(database, sql); return { database, command: result.command, rows: result.rows }; }, }, ]; } // The tools exist only when the server can be reached from the environment; without it, postgres // contributes none rather than failing the whole tool runtime. registerModuleTools("postgres", (env) => { try { return getPostgresTools(PostgresClient.fromEnv(env)); } catch { return []; } }); registerModuleTools("mesh-store", (env) => { try { return getStoreVerbs(PostgresClient.fromEnv(env)); } catch { return []; } });