From 41ebd39908ffc3428932411b5dc67ac77277bd77 Mon Sep 17 00:00:00 2001 From: Jordan Diaz Date: Sun, 19 Apr 2026 09:18:48 +0000 Subject: [PATCH] middleware --- mcp-server/tools/hooks/index.js | 144 ++++++++++++++++++++++++++++++++ mcp-server/tools/index.js | 2 + 2 files changed, 146 insertions(+) create mode 100644 mcp-server/tools/hooks/index.js diff --git a/mcp-server/tools/hooks/index.js b/mcp-server/tools/hooks/index.js new file mode 100644 index 0000000..25ceafd --- /dev/null +++ b/mcp-server/tools/hooks/index.js @@ -0,0 +1,144 @@ +import { z } from "zod"; +import { withAuth } from "../../auth/index.js"; +import { withAuthParams } from "../helpers/authSchema.js"; +import { handleToolError } from "../helpers/errorHandler.js"; +import { pythonGet, pythonPost } from "../helpers/pythonServerClient.js"; +import { getCurrentProjectInfo } from "../files/helpers.js"; +import { canEditCode } from "../helpers/roleCheck.js"; + +/** + * Tools para leer/escribir el `middleWare` de hooks globales del layout. + * + * El middleware vive en `layout.json["hooks"][i].middleWare` y determina cuando + * un hook global se ejecuta automaticamente antes de renderizar paginas: + * - [] → solo cuando se llama explicitamente. + * - ["allurls"] → antes de cada URL del sitio. + * - ["-", ...] → solo antes de ciertos registros. + */ + +function registerGetHookMiddlewareTool(server) { + server.tool( + "get_hook_middleware", + `Check which pages trigger a global hook as middleware. Middleware config determines whether the hook runs automatically BEFORE rendering specific pages (or all pages). Returns the list of middleware entries. + +Use this when the user asks about hook behavior or to verify config before changing it. + +hookEndPoint format: starts and ends with '/', with '/' as separator. E.g. file "hooks/hooks.parse_styles.php" → endPoint "/hooks/parse_styles/". + +Returns: +- middleWare: [] → hook only runs on explicit call (, Twig filter, CmsApi). +- middleWare: ["allurls"] → runs before every page. +- middleWare: ["cms_apartados-8", ...] → runs before those specific records ("-").`, + withAuthParams({ + hookEndPoint: z.string().describe('Hook endpoint path, e.g. "/hooks/parse_styles/"'), + }), + { readOnlyHint: true, destructiveHint: false }, + withAuth(async ({ hookEndPoint }, extra) => { + try { + const { projectSlug } = getCurrentProjectInfo(); + const result = await pythonGet("/api/creator/hook-middleware", { + project: projectSlug, + endPoint: hookEndPoint, + }); + if (!result?.success) { + return { + content: [{ + type: "text", + text: JSON.stringify({ + success: false, + error: result?.error || "No se pudo leer el middleware", + }), + }], + isError: true, + }; + } + return { + content: [{ + type: "text", + text: JSON.stringify({ + success: true, + exists: !!result.exists, + middleWare: result.middleWare || [], + hookEndPoint, + }, null, 2), + }], + }; + } catch (error) { + return handleToolError(error, "get_hook_middleware", { hookEndPoint }); + } + }) + ); +} + +function registerSetHookMiddlewareTool(server) { + server.tool( + "set_hook_middleware", + `Configure when a global hook runs automatically (middleware). This updates layout.json['hooks'][i].middleWare for the hook matching hookEndPoint. + +Use this AFTER creating or editing a hook file (via acai-write) if the hook should execute BEFORE rendering specific pages. The hook file alone is not enough — the file exists but won't auto-run as middleware without this config. + +middleWare values: +- [] → hook runs only when called explicitly (default for new hooks). +- ["allurls"] → runs before every page of the site. +- ["-", ...] → runs before specific records. Get num+tableName from the CMS records. + +Examples: +- Redirect logic that must run on the homepage only: middleWare=["cms_apartados-2"] (assuming num=2 is home). +- Global analytics injection: middleWare=["allurls"]. +- Just a reusable utility hook called from modules/twig: middleWare=[] (default).`, + withAuthParams({ + hookEndPoint: z.string().describe('Hook endpoint path, e.g. "/hooks/parse_styles/"'), + middleWare: z.array(z.string()).describe('Array de strings. Vacio, ["allurls"], o ["-", ...]'), + }), + { readOnlyHint: false, destructiveHint: false }, + withAuth(async ({ hookEndPoint, middleWare }, extra) => { + try { + const { projectSlug } = getCurrentProjectInfo(); + const result = await pythonPost("/api/creator/hook-middleware", { + project: projectSlug, + endPoint: hookEndPoint, + middleWare, + }); + if (!result?.success) { + return { + content: [{ + type: "text", + text: JSON.stringify({ + success: false, + error: result?.error || "No se pudo guardar", + }), + }], + isError: true, + }; + } + return { + content: [{ + type: "text", + text: JSON.stringify({ + success: true, + message: "Middleware actualizado", + middleWare: result.middleWare || [], + hookEndPoint, + }, null, 2), + }], + }; + } catch (error) { + return handleToolError(error, "set_hook_middleware", { hookEndPoint, middleWare }); + } + }) + ); +} + +/** + * Registra las tools de configuracion de hooks globales. + * + * `get_hook_middleware` es de solo lectura y se registra siempre. El set + * modifica el layout y solo se expone si el rol puede editar codigo — sigue + * el mismo criterio que otras tools de escritura (ver project/index.js). + */ +export function registerHookTools(server) { + registerGetHookMiddlewareTool(server); + if (canEditCode()) { + registerSetHookMiddlewareTool(server); + } +} diff --git a/mcp-server/tools/index.js b/mcp-server/tools/index.js index a17fb12..5e471aa 100644 --- a/mcp-server/tools/index.js +++ b/mcp-server/tools/index.js @@ -7,6 +7,7 @@ import { registerRemoteGitTools } from './remote_git/index.js'; import { registerNavigationTools } from './navigation/index.js'; import { registerProjectTools } from './project/index.js'; import { registerFileTools } from './files/index.js'; +import { registerHookTools } from './hooks/index.js'; /** * Register all tools on the MCP server @@ -21,4 +22,5 @@ export function registerTools(server) { registerNavigationTools(server); registerProjectTools(server); registerFileTools(server); + registerHookTools(server); }