Initial commit
This commit is contained in:
89
mcp-server/tools/records/list.js
Normal file
89
mcp-server/tools/records/list.js
Normal file
@@ -0,0 +1,89 @@
|
||||
import { z } from "zod";
|
||||
import { withAuth, getSessionCredentials } from "../../auth/index.js";
|
||||
import { handleToolError, validateRequired, handleApiResponse } from "../helpers/errorHandler.js";
|
||||
import { AcaiHttpClient } from "../helpers/acaiHttpClient.js";
|
||||
import { withAuthParams } from "../helpers/authSchema.js";
|
||||
|
||||
export function registerListTableRecordsTool(server) {
|
||||
server.tool(
|
||||
"list_table_records",
|
||||
"List or search records in a database table. Returns JSON. Default limit is 50 — request only what you need. Use 'fields' to return only the columns you need (saves tokens). ALWAYS use 'num' as primary key, NEVER 'id'. Upload fields are arrays with urlPath.",
|
||||
withAuthParams({
|
||||
tableName: z.string().describe("Name of the table (without 'cms_' prefix, e.g., 'productos')"),
|
||||
page: z.number().optional().describe("Page number (default: 1)"),
|
||||
where: z.string().optional().describe("SQL WHERE clause to filter records (e.g., \"name LIKE '%keyword%'\")"),
|
||||
limit: z.number().optional().describe("Max records to return. Default: 50. Use 5-10 for previews, up to 200 max for large exports."),
|
||||
fields: z.array(z.string()).optional().describe("Return only these columns (e.g., ['num', 'titulo', 'precio']). Omit to return all columns. Always include 'num' if you need record IDs."),
|
||||
truncateText: z.number().optional().describe("Truncate string field values longer than this many chars. Appends '... [truncated, N chars]'. Combine with 'fields' for maximum token savings."),
|
||||
}),
|
||||
{ readOnlyHint: true, destructiveHint: false },
|
||||
withAuth(async ({ tableName, page, where, limit, fields, truncateText }, extra) => {
|
||||
try {
|
||||
// Validate required parameters
|
||||
const validationError = validateRequired({ tableName }, ['tableName'], 'list_table_records');
|
||||
if (validationError) return validationError;
|
||||
|
||||
// Build payload for CMS API
|
||||
const credentials = await getSessionCredentials(extra.sessionId);
|
||||
const payload = {
|
||||
tableName: tableName,
|
||||
where: where || "",
|
||||
order: "",
|
||||
limit: limit || 50,
|
||||
options: {}
|
||||
};
|
||||
|
||||
// Send to CMS API via viewer_functions
|
||||
const response = await AcaiHttpClient.postCmsApi(
|
||||
credentials,
|
||||
'get',
|
||||
payload,
|
||||
credentials.token,
|
||||
credentials.tokenHash
|
||||
);
|
||||
|
||||
// Check for API errors
|
||||
const apiError = handleApiResponse(response.data, 'list_table_records');
|
||||
if (apiError) return apiError;
|
||||
|
||||
// Post-process: filter fields if requested
|
||||
let resultData = response.data;
|
||||
if (fields && fields.length > 0 && Array.isArray(resultData?.data)) {
|
||||
resultData = {
|
||||
...resultData,
|
||||
data: resultData.data.map(record =>
|
||||
Object.fromEntries(fields.map(f => [f, record[f]]))
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
// Post-process: truncate long text values if requested
|
||||
if (truncateText && truncateText > 0 && Array.isArray(resultData?.data)) {
|
||||
resultData = {
|
||||
...resultData,
|
||||
data: resultData.data.map(record => {
|
||||
const truncated = {};
|
||||
for (const [key, value] of Object.entries(record)) {
|
||||
if (typeof value === 'string' && value.length > truncateText) {
|
||||
truncated[key] = value.substring(0, truncateText) + `... [truncated, ${value.length} chars]`;
|
||||
} else {
|
||||
truncated[key] = value;
|
||||
}
|
||||
}
|
||||
return truncated;
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: JSON.stringify(resultData, null, 2)
|
||||
}],
|
||||
};
|
||||
} catch (error) {
|
||||
return handleToolError(error, 'list_table_records', { tableName, page });
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user