From 454330010110b15668efb8f23ad17a4975696199 Mon Sep 17 00:00:00 2001 From: Jordan Diaz Date: Fri, 12 Jun 2026 09:29:17 +0000 Subject: [PATCH] Fix upload_image_to_assets 404 en Forge (header Host) + guard data-URI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - saveFileBuilder (fileBuilder.js) hacía POST directo a viewer_functions.php sin header Host -> en Forge (api_web_url interno http://web:80) Apache servía el vhost por defecto -> 404. Ahora delega en AcaiHttpClient.postViewerAction, que resuelve api_web_url + Host: forge_host (igual que el resto de tools). Pasa credentials completo. - upload_record_image: rechaza data-URI/base64 con error claro (antes derivaba el nombre del base64 -> "File name too long" en mcp_respond.php). Co-Authored-By: Claude Opus 4.8 (1M context) --- mcp-server/tools/helpers/fileBuilder.js | 34 +++++++++---------- mcp-server/tools/media/upload.js | 19 +++++++++++ mcp-server/tools/media/uploadImageToAssets.js | 2 +- 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/mcp-server/tools/helpers/fileBuilder.js b/mcp-server/tools/helpers/fileBuilder.js index f559e87..639b62f 100644 --- a/mcp-server/tools/helpers/fileBuilder.js +++ b/mcp-server/tools/helpers/fileBuilder.js @@ -1,11 +1,13 @@ -import axios from "axios"; +import { AcaiHttpClient } from "./acaiHttpClient.js"; /** - * Helper to save files using saveFileBuilder action + * Helper to save files using saveFileBuilder action. + * Delega en AcaiHttpClient.postViewerAction, que construye la URL con + * api_web_url + el header Host (forge_host) y aplica assertSafeCmsTarget. * Used by multiple tools (save.js, saveGeneralSection.js, write.js, etc.) - * + * * @param {Object} params - * @param {string} params.web_url - URL base del sitio (ej: http://localhost:PORT) + * @param {Object} params.credentials - Target completo (web_url, api_web_url, forge_host, mode) * @param {string} params.token - Session token * @param {string} params.tokenHash - Token hash * @param {string} params.path - Folder path (e.g., '/modulos/mymodule/') @@ -14,7 +16,7 @@ import axios from "axios"; * @returns {Promise} Response from the API */ export async function saveFileBuilder({ - web_url, + credentials, token, tokenHash, path, @@ -26,12 +28,7 @@ export async function saveFileBuilder({ return null; } - const viewerUrl = web_url + '/cms/lib/viewer_functions.php'; - const payload = { - action_ws: 'saveFileBuilder', - token: token, - tokenHash: tokenHash, fileName: fileName, content: content, rawDataSended: rawDataSended, @@ -39,14 +36,17 @@ export async function saveFileBuilder({ path: path }; - console.error(`[saveFileBuilder] URL: ${viewerUrl}`); console.error(`[saveFileBuilder] Path: ${path}`); console.error(`[saveFileBuilder] Content length: ${content.length} chars`); try { - const response = await axios.post(viewerUrl, payload, { - headers: { "Content-Type": "application/json" } - }); + const response = await AcaiHttpClient.postViewerAction( + credentials, + 'saveFileBuilder', + payload, + token, + tokenHash + ); console.error(`[saveFileBuilder] Response for ${fileName}:`, JSON.stringify(response.data, null, 2)); @@ -69,7 +69,7 @@ export async function saveFileBuilder({ * Helper to save multiple files at once * * @param {Object} params - * @param {string} params.web_url - URL base del sitio (ej: http://localhost:PORT) + * @param {Object} params.credentials - Target completo (web_url, api_web_url, forge_host, mode) * @param {string} params.token - Session token * @param {string} params.tokenHash - Token hash * @param {string} params.path - Folder path (e.g., '/modulos/mymodule/') @@ -77,7 +77,7 @@ export async function saveFileBuilder({ * @returns {Promise} Results for each file */ export async function saveMultipleFiles({ - web_url, + credentials, token, tokenHash, path, @@ -88,7 +88,7 @@ export async function saveMultipleFiles({ for (const [fileName, content] of Object.entries(files)) { if (content) { results[fileName] = await saveFileBuilder({ - web_url, + credentials, token, tokenHash, path, diff --git a/mcp-server/tools/media/upload.js b/mcp-server/tools/media/upload.js index 3bfd6d1..2f6dbd3 100644 --- a/mcp-server/tools/media/upload.js +++ b/mcp-server/tools/media/upload.js @@ -119,6 +119,25 @@ export function registerUploadRecordImageTool(server) { ); if (validationError) return validationError; + // Rechazar data-URI / base64 crudo: derivar el nombre de fichero + // del base64 produce nombres de miles de chars que revientan + // file_put_contents ("File name too long"). Exigir URL http real. + const trimmedImage = imageUrl.trim(); + const isHttpUrl = /^https?:\/\//i.test(trimmedImage); + const isFsPath = trimmedImage.startsWith("/") && !trimmedImage.startsWith("//"); + if (!isHttpUrl && !isFsPath) { + return { + content: [{ + type: "text", + text: JSON.stringify({ + error: "imageUrl must be an http(s) URL, not a data URI or raw base64. " + + "First upload the image with the 'upload_image_to_assets' tool and pass the returned imageUrl here." + }, null, 2) + }], + isError: true, + }; + } + const projectSlug = path.basename(resolveCurrentProjectDir()); // Intentar via Python server (tiene sync + optimizacion) diff --git a/mcp-server/tools/media/uploadImageToAssets.js b/mcp-server/tools/media/uploadImageToAssets.js index 3c597db..c6b377b 100644 --- a/mcp-server/tools/media/uploadImageToAssets.js +++ b/mcp-server/tools/media/uploadImageToAssets.js @@ -154,7 +154,7 @@ export function registerUploadImageToAssetsTool(server) { // Upload using saveFileBuilder const uploadResult = await saveFileBuilder({ - web_url: credentials.api_web_url || credentials.web_url, + credentials, token: credentials.token, tokenHash: credentials.tokenHash, path: assetsPath,