Files
mesh-catalog/modules/postgres/tools/index.ts
T
jschoubben 4c7e438ea3 postgres serves the store seat's verbs, databases and query, and lists its tools (hq ADR 0159)
Named as the seat names them so the runtime finds them by name; the same calls as its own tools.
Its definition now lists its tools, which is what holding a seat with verbs demands at registration.
Merge before the controller declares the verbs on the mesh-store seat.
2026-10-01 14:02:41 +02:00

71 lines
3.2 KiB
TypeScript

// 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 [
// The store seat's verbs (novox/hq ADR 0159), served by this module wherever it holds the seat:
// named as the seat names them, so the runtime finds them by name, and answering with the same
// calls as this module's own tools below. A holder that is not the store serves nothing here;
// the bus admits the seat's subjects only to the holder.
{
name: "databases",
description: "The store seat's verb: every database the store holds, with its on-disk size.",
input: {},
run: async () => ({ databases: await postgres.listDatabases() }),
},
{
name: "query",
description: "The store seat's verb: 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 };
},
},
{
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 (wrapped in a read-only transaction).",
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 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 [];
}
});