// minio's tools — ported here from the shared sdk (novox/hq ADR 0044), importing minio's own client. // They return structured data; the mesh serves them through the sdk's tool harness. These are the // read/inspect operations useful to an operator; creating storage for a consumer is the provisioner's // job, not a tool's. import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools"; import { MinioClient } from "../client.js"; export function getMinioTools(minio: MinioClient): ToolDefinition[] { return [ { name: "minio_list_buckets", description: "List every S3 bucket in the object store, with creation dates.", input: {}, run: async () => { const buckets = await minio.listBuckets(); return { count: buckets.length, buckets: buckets.map((b) => ({ name: b.name, createdAt: b.creationDate?.toISOString() })), }; }, }, { name: "minio_list_objects", description: "List objects in a bucket, optionally under a prefix.", input: { bucket: { type: "string", description: "the bucket name" }, prefix: { type: "string", description: "only keys under this prefix (e.g. 'photos/')" }, recursive: { type: "boolean", description: "descend into nested prefixes (default false)" }, limit: { type: "number", description: "max keys to return (default 100)" }, }, run: async (args) => { const bucket = String(args.bucket); const objects = await minio.listObjects( bucket, args.prefix ? String(args.prefix) : "", Boolean(args.recursive), args.limit ? Number(args.limit) : 100, ); return { bucket, count: objects.length, objects: objects.map((o) => ({ key: o.name, size: o.size, lastModified: o.lastModified.toISOString(), etag: o.etag })), }; }, }, { name: "minio_bucket_info", description: "Summary of one bucket: whether it exists, its region, and a sampled object count and size.", input: { bucket: { type: "string", description: "the bucket name" } }, run: async (args) => minio.bucketInfo(String(args.bucket)), }, { name: "minio_presigned_url", description: "A time-limited URL to download (GET) or upload (PUT) one object without credentials.", input: { bucket: { type: "string", description: "the bucket name" }, object: { type: "string", description: "the object key" }, method: { type: "string", description: "'GET' to download (default) or 'PUT' to upload" }, expires: { type: "number", description: "seconds until the URL expires (default 86400 = 24h)" }, }, run: async (args) => { const method = String(args.method ?? "GET").toUpperCase() === "PUT" ? "PUT" : "GET"; const expires = args.expires ? Number(args.expires) : 86400; const url = minio.presignedUrl(method, String(args.bucket), String(args.object), expires); return { method, bucket: String(args.bucket), object: String(args.object), expiresInSeconds: expires, url }; }, }, ]; } // The tools exist only when the object store is configured and reachable; without it minio // contributes none rather than failing the whole tool runtime. registerModuleTools("minio", (env) => { try { return getMinioTools(MinioClient.fromEnv(env)); } catch { return []; } });