Initial commit
This commit is contained in:
145
mcp-server/tools/records/createUpdate.js
Normal file
145
mcp-server/tools/records/createUpdate.js
Normal file
@@ -0,0 +1,145 @@
|
||||
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 { table } from "console";
|
||||
import { withAuthParams } from "../helpers/authSchema.js";
|
||||
|
||||
export function registerCreateOrUpdateRecordTool(server) {
|
||||
server.tool(
|
||||
"create_or_update_record",
|
||||
`Create or update records in a database table. Before using: read resource 'acai-cheat-sheet' for domain rules, then check table schema with get_table_schema.
|
||||
|
||||
Key rules: tables without 'cms_' prefix, primary key is 'num', uploads are arrays (use upload_record_image after creating record), dates as YYYY-MM-DD HH:mm:ss, checkboxes as 1/0, enlace as /path/.
|
||||
|
||||
For builder tables (e.g. 'apartados'): must include num:null, builder:"[]", controlador, precontrolador, breadcrumb, enlace fields. See resource 'guia-registros' for full field type reference.`,
|
||||
withAuthParams({
|
||||
tableName: z.string().describe("Name of the table (without 'cms_' prefix, e.g., 'productos', 'equipo')"),
|
||||
recordId: z.any().optional().describe("Record ID for updating. Leave empty to create new record. NOT USED when records is an array."),
|
||||
fields: z.any().describe("Single record object OR array of record objects for batch insert. Example: { nombre: 'Product 1' } or [{ nombre: 'Product 1' }, { nombre: 'Product 2' }]. IMPORTANT: Always consult 'guia-registros' for field types and formats and check if is table with builder fields."),
|
||||
tableSchema: z.any().describe("Provide the table schema object to validate field types before sending to API. If not provided, schema will not be validated."),
|
||||
}),
|
||||
{ readOnlyHint: false, destructiveHint: false },
|
||||
withAuth(async ({ tableName, recordId, fields }, extra) => {
|
||||
try {
|
||||
// Validate required parameters
|
||||
const validationError = validateRequired({ tableName, fields }, ['tableName', 'fields'], 'create_or_update_record');
|
||||
if (validationError) return validationError;
|
||||
|
||||
// if fields is string, try to parse as JSON
|
||||
if (typeof fields === 'string') {
|
||||
try {
|
||||
fields = JSON.parse(fields);
|
||||
} catch (e) {
|
||||
return {
|
||||
content: [{ type: "text", text: "Error: 'fields' parameter is a string but not valid JSON." }],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
// Determine if fields is array or single object
|
||||
const isArray = Array.isArray(fields);
|
||||
const recordsArray = isArray ? fields : [fields];
|
||||
|
||||
// Check if trying to update with array (not supported)
|
||||
if (isArray && recordId) {
|
||||
return {
|
||||
content: [{ type: "text", text: "Error: Cannot use recordId when fields is an array. Use fields as array for batch insert only." }],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
|
||||
// Protect critical fields during updates — these should never be changed by AI
|
||||
const PROTECTED_UPDATE_FIELDS = ['enlace', 'controlador', 'precontrolador'];
|
||||
if (recordId) {
|
||||
// On update: strip protected fields silently
|
||||
recordsArray.forEach(record => {
|
||||
PROTECTED_UPDATE_FIELDS.forEach(f => {
|
||||
if (f in record) delete record[f];
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Process enlace field for new records only
|
||||
let processedRecords = recordsArray;
|
||||
if (!recordId) {
|
||||
processedRecords = recordsArray.map(record => {
|
||||
let enlaceValue = record.enlace;
|
||||
|
||||
if (!enlaceValue) {
|
||||
// Generate random enlace if not provided to ensure uniqueness
|
||||
enlaceValue = '/' + Math.random().toString(36).substring(2, 10) + '/';
|
||||
} else {
|
||||
// Ensure format /.../
|
||||
enlaceValue = String(enlaceValue);
|
||||
if (!enlaceValue.startsWith('/')) enlaceValue = '/' + enlaceValue;
|
||||
if (!enlaceValue.endsWith('/')) enlaceValue = enlaceValue + '/';
|
||||
}
|
||||
|
||||
return { ...record, enlace: enlaceValue };
|
||||
});
|
||||
}
|
||||
|
||||
// Prepare payload for CMS API
|
||||
const credentials = await getSessionCredentials(extra.sessionId);
|
||||
const recordPayload = {
|
||||
tableName: tableName,
|
||||
records: processedRecords,
|
||||
functions: [],
|
||||
options: {}
|
||||
};
|
||||
|
||||
// Determine action: insert for new records, update for existing
|
||||
const isNewRecord = !recordId;
|
||||
let response;
|
||||
|
||||
if (isNewRecord) {
|
||||
// Insert new record(s)
|
||||
response = await AcaiHttpClient.postCmsApi(
|
||||
credentials,
|
||||
'insert',
|
||||
recordPayload,
|
||||
credentials.token,
|
||||
credentials.tokenHash
|
||||
);
|
||||
} else {
|
||||
// Update existing record (only single record, not array)
|
||||
response = await AcaiHttpClient.postCmsApi(
|
||||
credentials,
|
||||
'update',
|
||||
{
|
||||
...recordPayload,
|
||||
where: `num = ${recordId}`
|
||||
},
|
||||
credentials.token,
|
||||
credentials.tokenHash
|
||||
);
|
||||
}
|
||||
|
||||
// Check for API errors
|
||||
const apiError = handleApiResponse(response.data, 'create_or_update_record');
|
||||
if (apiError) return apiError;
|
||||
|
||||
return {
|
||||
content: [{
|
||||
type: "text",
|
||||
text: JSON.stringify({
|
||||
success: true,
|
||||
message: isNewRecord
|
||||
? `${isArray ? recordsArray.length : 1} record(s) created successfully`
|
||||
: `Record ${recordId} updated successfully`,
|
||||
tableName: tableName,
|
||||
recordIds: response.data?.data || (recordId || 'new'),
|
||||
recordsCount: isArray ? recordsArray.length : 1,
|
||||
createdIds: response.data?.data,
|
||||
suggestion: isNewRecord && !isArray ? `You can verify the record by fetching: ${credentials.web_url}${processedRecords[0].enlace}` : undefined
|
||||
}, null, 2)
|
||||
}],
|
||||
};
|
||||
} catch (error) {
|
||||
return handleToolError(error, 'create_or_update_record', { tableName, recordId, isArray: Array.isArray(fields) });
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user