From 224ac2dad7ee62fdc580caab51f03a298f6fbeca Mon Sep 17 00:00:00 2001 From: Jordan Diaz Date: Fri, 10 Apr 2026 16:52:00 +0000 Subject: [PATCH] Control de modo editor/admin produccion/local --- mcp-server/auth/credentials.js | 13 ++++++++++--- mcp-server/tools/files/index.js | 11 ++++++++--- mcp-server/tools/helpers/roleCheck.js | 24 ++++++++++++++++++++++++ mcp-server/tools/modules/index.js | 5 ++++- mcp-server/tools/project/index.js | 7 +++++-- mcp-server/tools/remote_git/index.js | 5 ++++- 6 files changed, 55 insertions(+), 10 deletions(-) create mode 100644 mcp-server/tools/helpers/roleCheck.js diff --git a/mcp-server/auth/credentials.js b/mcp-server/auth/credentials.js index e865c0e..d6f30ca 100644 --- a/mcp-server/auth/credentials.js +++ b/mcp-server/auth/credentials.js @@ -18,6 +18,13 @@ import { fetchProjectInfo } from "./localClient.js"; const DEFAULT_ROLE = 'developer'; const FORGE_INTERNAL_URL = process.env.ACAI_FORGE_WEB_URL || "http://web:80"; +const resolveEffectiveRole = (baseRole) => { + if (process.env.ACAI_ROLE_OVERRIDE) return process.env.ACAI_ROLE_OVERRIDE; + if (process.env.ACAI_MODE_OVERRIDE === "production") return "editor"; + if (process.env.ACAI_MODE === "production") return "editor"; + return baseRole || DEFAULT_ROLE; +}; + // Session-based credentials storage (ephemeral, per-session) export const sessionCredentials = new Map(); @@ -99,7 +106,7 @@ const readProjectAcaiFallback = () => { tokenHash: data.tokenHash || process.env.ACAI_TOKEN_HASH || null, mode, profileName: "acai-file", - role: DEFAULT_ROLE, + role: resolveEffectiveRole(data.role), }; } catch (error) { console.error(`[Credentials] Failed to read .acai fallback: ${error.message}`); @@ -136,7 +143,7 @@ const resolveLocalProjectFallback = async () => { tokenHash: info.tokenHash || process.env.ACAI_TOKEN_HASH || null, mode, profileName: "project-info", - role: DEFAULT_ROLE, + role: resolveEffectiveRole(info.role), }; } } catch (error) { @@ -279,7 +286,7 @@ export const getSessionCredentials = async (sessionId, inlineCredentials = null) forge_host: process.env.ACAI_FORGE_HOST || null, tokenHash: process.env.ACAI_TOKEN_HASH || null, profileName: 'default', - role: 'developer', // Env fallback = local dev, full access + role: resolveEffectiveRole('developer'), // Env fallback = local dev, full access }; }; diff --git a/mcp-server/tools/files/index.js b/mcp-server/tools/files/index.js index 143a1f2..d35da66 100644 --- a/mcp-server/tools/files/index.js +++ b/mcp-server/tools/files/index.js @@ -4,12 +4,17 @@ import { registerAcaiLineReplaceTool } from "./lineReplace.js"; import { registerAcaiDeleteTool } from "./delete.js"; import { registerAcaiGlobTool } from "./glob.js"; import { registerAcaiGrepTool } from "./grep.js"; +import { canEditCode } from "../helpers/roleCheck.js"; export function registerFileTools(server) { + // Lectura: siempre disponible registerAcaiViewTool(server); registerAcaiGlobTool(server); registerAcaiGrepTool(server); - registerAcaiWriteTool(server); - registerAcaiLineReplaceTool(server); - registerAcaiDeleteTool(server); + // Escritura: solo si el rol puede editar codigo + if (canEditCode()) { + registerAcaiWriteTool(server); + registerAcaiLineReplaceTool(server); + registerAcaiDeleteTool(server); + } } diff --git a/mcp-server/tools/helpers/roleCheck.js b/mcp-server/tools/helpers/roleCheck.js new file mode 100644 index 0000000..5792e6e --- /dev/null +++ b/mcp-server/tools/helpers/roleCheck.js @@ -0,0 +1,24 @@ +/** + * Helper central para determinar el rol efectivo del MCP y bloquear tools + * peligrosas cuando el user es "editor". + * + * El rol se recibe principalmente via env var ACAI_ROLE_OVERRIDE inyectada + * por el backend Python (agentic.py y cronjobs.py). Hay autoderivacion + * defensiva en caso de que alguien lance el MCP sin el override: + * - Si ACAI_MODE(_OVERRIDE) = "production" → rol editor por defecto. + * - Si no → rol developer. + */ +export function getEffectiveRole() { + if (process.env.ACAI_ROLE_OVERRIDE) return process.env.ACAI_ROLE_OVERRIDE; + if (process.env.ACAI_MODE_OVERRIDE === "production") return "editor"; + if (process.env.ACAI_MODE === "production") return "editor"; + return "developer"; +} + +/** + * True si el rol efectivo puede editar archivos de codigo. + * Los roles permitidos son todo lo que NO sea "editor". + */ +export function canEditCode() { + return getEffectiveRole() !== "editor"; +} diff --git a/mcp-server/tools/modules/index.js b/mcp-server/tools/modules/index.js index 2ab7036..77a8067 100644 --- a/mcp-server/tools/modules/index.js +++ b/mcp-server/tools/modules/index.js @@ -1,9 +1,12 @@ import { registerCheckModuleTool } from './check.js'; import { registerCheckModuleUsageTool } from './checkUsage.js'; import { registerCompileModuleTool } from './compile.js'; +import { canEditCode } from '../helpers/roleCheck.js'; export function registerModuleTools(server) { registerCheckModuleTool(server); registerCheckModuleUsageTool(server); - registerCompileModuleTool(server); + if (canEditCode()) { + registerCompileModuleTool(server); + } } diff --git a/mcp-server/tools/project/index.js b/mcp-server/tools/project/index.js index e11b10a..51dcd71 100644 --- a/mcp-server/tools/project/index.js +++ b/mcp-server/tools/project/index.js @@ -1,7 +1,10 @@ import { registerSaveProjectStylesTool } from "./saveStyles.js"; import { registerGetWebUrlTool } from "./getWebUrl.js"; +import { canEditCode } from "../helpers/roleCheck.js"; export function registerProjectTools(server) { - registerSaveProjectStylesTool(server); - registerGetWebUrlTool(server); + registerGetWebUrlTool(server); // siempre + if (canEditCode()) { + registerSaveProjectStylesTool(server); + } } diff --git a/mcp-server/tools/remote_git/index.js b/mcp-server/tools/remote_git/index.js index d7aed26..21122d3 100644 --- a/mcp-server/tools/remote_git/index.js +++ b/mcp-server/tools/remote_git/index.js @@ -1,5 +1,8 @@ import { registerRecoverGitTools } from './rollback.js'; +import { canEditCode } from '../helpers/roleCheck.js'; export function registerRemoteGitTools(server) { - registerRecoverGitTools(server); + if (canEditCode()) { + registerRecoverGitTools(server); + } }