@budibase/server
Version:
Budibase Web Server
131 lines (124 loc) • 5.05 kB
text/typescript
import { z } from "zod"
import { newTool } from ".."
import sdk from "../../../sdk"
import { RowSearchParams, SortOrder } from "@budibase/types"
export default [
newTool({
name: "list_rows",
description: "List rows in a given table",
parameters: z.object({
tableId: z.string().describe("The ID of the table to list rows from"),
limit: z.number().nullish().describe("Maximum number of rows to return"),
startKey: z.string().nullish().describe("Start key for pagination"),
}),
handler: async ({ tableId }) => {
const rows = await sdk.rows.fetch(tableId)
return { rows }
},
}),
newTool({
name: "get_row",
description: "Get a specific row by ID",
parameters: z.object({
tableId: z.string().describe("The ID of the table"),
rowId: z.string().describe("The ID of the row to retrieve"),
}),
handler: async ({ tableId, rowId }) => {
const row = await sdk.rows.find(tableId, rowId)
return { row }
},
}),
newTool({
name: "create_row",
description:
"Create a new row. Only include fields that match the table schema. " +
"CRITICAL: Use plain text values only. Do NOT include HTML tags, markdown formatting, " +
"or content containing quotes, backslashes, or special characters - these break JSON parsing. " +
"Summarize or strip complex content. Max 500 characters per field.",
parameters: z.object({
tableId: z.string().describe("The ID of the table to create the row in"),
data: z
.record(z.string(), z.any())
.describe(
'Row data as a JSON object. Example: {"name": "John", "age": 30}. ' +
"Do NOT nest this inside a string; pass the object directly. " +
"Values must be plain text - no HTML, markdown, or special characters."
),
}),
handler: async ({ tableId, data }) => {
const row = await sdk.rows.save(tableId, data as any, undefined)
return { row }
},
}),
newTool({
name: "update_row",
description:
"Update an existing row. " +
"CRITICAL: Use plain text values only. Do NOT include HTML tags, markdown formatting, " +
"or content containing quotes, backslashes, or special characters - these break JSON parsing. " +
"Summarize or strip complex content. Max 500 characters per field.",
parameters: z.object({
tableId: z.string().describe("The ID of the table"),
rowId: z.string().describe("The ID of the row to update"),
data: z
.record(z.string(), z.any())
.describe(
'The updated data as a JSON object. Example: {"name": "Jane"}. ' +
"Do NOT nest this inside a string; pass the object directly. " +
"Values must be plain text - no HTML, markdown, or special characters."
),
}),
handler: async ({ tableId, rowId, data }) => {
const rowData = { ...data, _id: rowId }
const row = await sdk.rows.save(tableId, rowData, undefined)
return { row }
},
}),
newTool({
name: "search_rows",
description:
"Search for rows in a table based on criteria. " +
"IMPORTANT: You can ONLY filter on fields that exist in the table schema. " +
"Use get_table or list_tables first to see available field names. " +
"Searching on non-existent fields will fail.",
parameters: z.object({
tableId: z.string().describe("The ID of the table to search"),
query: z
.record(z.string(), z.any())
.nullish()
.describe(
`Query filters object. Structure: { operator: { fieldName: value } }. ` +
`CRITICAL: fieldName must match an existing column in the table schema exactly (case-sensitive). ` +
`Valid operators: "equal", "notEqual", "empty", "notEmpty", "fuzzy", "string", "contains", "notContains", "containsAny", "oneOf", "range". ` +
`Examples: ` +
`Find where status equals "active": {"equal": {"status": "active"}}. ` +
`Find where name is not empty: {"notEmpty": {"name": true}}. ` +
`Find where price is within the range of 10 to 100: {"range": {"price": {"low": 10, "high": 100}}}.`
),
sort: z
.object({
column: z.string().describe("Column to sort by"),
order: z.enum(["ascending", "descending"]).describe("Sort order"),
})
.nullish()
.describe("Sort configuration"),
limit: z.number().nullish().describe("Maximum number of results"),
}),
handler: async ({ tableId, query, sort, limit }) => {
const searchParams: RowSearchParams = {
tableId,
query: query || {},
limit: limit ?? undefined,
}
if (sort) {
searchParams.sort = sort.column
searchParams.sortOrder =
sort.order === SortOrder.ASCENDING
? SortOrder.ASCENDING
: SortOrder.DESCENDING
}
const result = await sdk.rows.search(searchParams)
return { rows: result.rows }
},
}),
]