Files
agenticSystem/mcp-server/tools/records/getModuleConfigVars.js
Jordan Diaz bdee7665ec feat: el agente puede declarar y leer las cmsTables de un modulo
Las cmsTables (tablas del CMS cuyo contenido MUESTRA un modulo) ya se
definen desde Forge y desde el CMS legacy; faltaba que el agente las
entendiera.

- update_module_metadata acepta cmsTables. El endpoint del server ya la
  validaba, asi que solo habia que declararla en el schema Zod.
- get_module_config_vars la devuelve: sale gratis porque el handler ya
  resolvia el schema del modulo para uploadFields/varsMeta.
- Docs (01, 03, 09) con el matiz que mas se puede confundir: `tables` es
  donde viven los VALORES de las vars (siempre builder_custom, lo pone el
  compilador) y `cmsTables` es que contenido MUESTRA el modulo.

Para el agente lo util no es solo escribirlas: al recibirlas sabe donde
esta de verdad el contenido visible. Si le piden cambiar lo que muestra un
listado de noticias, los registros estan en esa tabla, no en las vars del
modulo.

Se documenta ademas que la metadata del builder.json es la UNICA parte
editable del fichero (y solo con esta tool): el resto lo regenera el
compilador en cada compilacion.
2026-08-12 11:32:07 +00:00

98 lines
5.1 KiB
JavaScript

import { z } from "zod";
import { withAuth } from "../../auth/index.js";
import { withAuthParams } from "../helpers/authSchema.js";
import { handleToolError, validateRequired } from "../helpers/errorHandler.js";
import { pythonGet } from "../helpers/pythonServerClient.js";
import { getCurrentProjectInfo } from "../files/helpers.js";
// get_module_config_vars
//
// Pasa por el server Python (/api/creator/get-module-vars) en lugar de ir
// directamente al PHP. Asi obtenemos `varsMeta`: para cada variable del
// modulo, su ubicacion fisica en `builder_custom` (recordNum + columna real).
// Sin esto el agente solo conoce el nombre humano de la variable (ej.
// `titulo`) y al hacer create_or_update_record adivina el campo y suele
// fallar silenciosamente porque la columna real es algo como `title2`.
export function registerGetModuleConfigVarsTool(server) {
server.tool(
"get_module_config_vars",
`Get the current configuration variable values for a module instance on a page record. Returns:
- vars: resolved values (text, HTML, etc.) for simple vars and arrays for multi/repeater vars
- varsMeta: per-var physical location { tableName: 'builder_custom', recordNum, fieldName, type }. USE THIS to know exactly which row + column to update with create_or_update_record. The variable's display name (e.g. 'titulo') is NOT the same as the physical column name (e.g. 'title2'). Uploads inside a 'multi' var carry their labels in subFields.<subVar>.infoLabels.
- uploadFields: per-var upload location for upload_record_image / replace_record_image / set_upload_info. Each entry includes infoLabels when the module defines them (array; position 0 = info1).
- cmsTables: CMS tables whose records this module DISPLAYS (e.g. ["noticias"]), or [] if it only shows its own variables. This is where the module's visible content really lives: if the user asks to change what a news-list module shows, the records are in those tables, NOT in the module's vars. Use list_table_records / create_or_update_record against them. Empty list means nothing to look up elsewhere.
- moduleId, sectionId
Required params:
- tableName (string) without 'cms_' prefix (parent page table, e.g. 'apartados')
- recordNum (number) parent record primary key ('num', never 'id')
- sectionId (string) section ID of the module instance`,
withAuthParams({
tableName: z.string().describe("Parent table name (e.g. 'apartados')"),
recordNum: z.number().describe("Parent record number"),
sectionId: z.string().describe("Section ID of the module instance"),
}),
{ readOnlyHint: true, destructiveHint: false },
withAuth(async ({ tableName, recordNum, sectionId }, _extra) => {
try {
const validationError = validateRequired(
{ tableName, recordNum, sectionId },
["tableName", "recordNum", "sectionId"],
"get_module_config_vars"
);
if (validationError) return validationError;
const { projectSlug } = getCurrentProjectInfo();
const result = await pythonGet("/api/creator/get-module-vars", {
project: projectSlug,
table: tableName,
num: recordNum,
sectionId,
});
// El endpoint Python devuelve la respuesta del PHP envuelta:
// { success, data: { moduleId, vars, configVars, uploadFields, varsMeta, ... } }
// o directamente las claves en raiz. Normalizamos.
const inner = result?.data && typeof result.data === "object" ? result.data : result;
if (!inner || inner.success === false) {
return {
content: [{
type: "text",
text: JSON.stringify({
success: false,
error: inner?.error || "Could not read module config vars",
}),
}],
isError: true,
};
}
return {
content: [{
type: "text",
text: JSON.stringify({
success: true,
action: "get_module_config_vars",
tableName,
recordNum,
sectionId,
data: {
moduleId: inner.moduleId,
sectionId: inner.sectionId,
vars: inner.vars || {},
varsMeta: inner.varsMeta || {},
uploadFields: inner.uploadFields || {},
configVars: inner.configVars || {},
},
}, null, 2),
}],
};
} catch (error) {
return handleToolError(error, "get_module_config_vars", { tableName, recordNum, sectionId });
}
})
);
}