$ desarrollomcp
Volver al blog
Avanzado

Ejecución de Código con MCP: Construyendo Agentes Más Eficientes

Por DesarrolloMCP
|
#mcp #agentes #optimizacion #tokens #code-execution

Introducción

Una de las técnicas más prometedoras para mejorar la eficiencia de los agentes de IA es la ejecución de código. Este enfoque permite que los agentes interactúen con servidores MCP de manera más inteligente, reduciendo drásticamente el consumo de tokens.

En este artículo exploramos cómo implementar esta técnica basándonos en el artículo de ingeniería de Anthropic sobre el tema.

El problema: Sobrecarga de contexto

Los clientes MCP actuales enfrentan dos problemas fundamentales de eficiencia:

1. Sobrecarga de definiciones de herramientas

Los clientes MCP típicamente cargan todas las definiciones de herramientas en el contexto simultáneamente. Esto puede consumir cientos de miles de tokens antes de procesar cualquier solicitud real.

// Problema: Cargar todas las herramientas a la vez
const allTools = await mcpClient.listTools();
// Puede ser 100+ herramientas con schemas complejos
// = Miles de tokens consumidos innecesariamente

2. Resultados intermedios redundantes

Cuando un modelo debe pasar datos entre llamadas de herramientas, información como transcripciones completas fluye múltiples veces a través del contexto:

// Flujo ineficiente
const transcript = await getTranscript(); // 50,000 tokens
const summary = await summarize(transcript); // +50,000 tokens
const action = await createAction(summary); // +tokens

// Total: 100,000+ tokens desperdiciados

La solución: Servidores MCP como APIs ejecutables

La clave está en presentar los servidores MCP como APIs de código ejecutable en lugar de llamadas de herramientas directas. El agente escribe código que:

  1. Carga solo las definiciones necesarias
  2. Filtra y procesa datos localmente
  3. Devuelve resultados resumidos al modelo

Beneficios principales

Divulgación progresiva

Los modelos descubren herramientas navegando sistemas de archivos según necesidad:

// Estructura de descubrimiento
servers/
├── google-drive/
│   ├── getDocument.ts
│   └── listFiles.ts
└── salesforce/
    ├── updateRecord.ts
    └── queryRecords.ts

// El agente solo carga lo que necesita
import { getDocument } from "./servers/google-drive/getDocument";

Eficiencia contextual

Los agentes filtran y transforman resultados en código antes de devolverlos:

// En lugar de devolver toda la transcripción al modelo
const transcript = await gdrive.getDocument({
  documentId: 'abc123'
});

// Procesar localmente y devolver solo lo relevante
const actionItems = transcript
  .split('\n')
  .filter(line => line.includes('ACTION:'))
  .map(line => line.replace('ACTION:', '').trim());

return actionItems; // Solo los items de acción

Privacidad mejorada

Los datos sensibles permanecen en el entorno de ejecución y nunca atraviesan el modelo:

// Los datos sensibles se procesan localmente
const customerData = await crm.getCustomer({ id: '123' });
const anonymized = {
  tier: customerData.tier,
  totalPurchases: customerData.purchases.length,
  // Sin exponer: nombre, email, tarjeta, etc.
};
return anonymized;

Persistencia de estado

Los agentes mantienen estado entre operaciones y pueden guardar “skills” reutilizables:

// Skill reutilizable guardada
async function updateSalesforceFromGDrive(docId: string, recordId: string) {
  const transcript = (await gdrive.getDocument({
    documentId: docId
  })).content;

  await salesforce.updateRecord({
    objectType: 'SalesMeeting',
    recordId: recordId,
    data: { Notes: transcript }
  });
}

Implementación práctica

Estructura de proyecto

mcp-code-executor/
├── servers/
│   ├── google-drive/
│   │   └── getDocument.ts
│   ├── salesforce/
│   │   └── updateRecord.ts
│   └── index.ts         # Registro de servidores
├── skills/
│   └── saved-skills.ts  # Skills reutilizables
├── executor/
│   └── sandbox.ts       # Entorno de ejecución
└── agent.ts             # Lógica del agente

Ejemplo de servidor wrapeado

// servers/google-drive/getDocument.ts
export interface GetDocumentArgs {
  documentId: string;
}

export interface GetDocumentResult {
  content: string;
  metadata: {
    title: string;
    lastModified: string;
  };
}

export async function getDocument(
  args: GetDocumentArgs
): Promise<GetDocumentResult> {
  // Implementación real conectando a Google Drive API
  const doc = await googleDriveAPI.files.get({
    fileId: args.documentId,
    fields: 'name,modifiedTime'
  });

  const content = await googleDriveAPI.files.export({
    fileId: args.documentId,
    mimeType: 'text/plain'
  });

  return {
    content: content.data,
    metadata: {
      title: doc.data.name,
      lastModified: doc.data.modifiedTime
    }
  };
}

Ejecutor con sandbox

// executor/sandbox.ts
import { VM } from 'vm2';

export async function executeCode(code: string, context: object) {
  const vm = new VM({
    timeout: 30000,
    sandbox: {
      ...context,
      console: {
        log: (...args) => results.push({ type: 'log', data: args }),
        error: (...args) => results.push({ type: 'error', data: args })
      }
    }
  });

  const results: any[] = [];

  try {
    const output = await vm.run(`
      (async () => {
        ${code}
      })()
    `);

    return { success: true, output, logs: results };
  } catch (error) {
    return { success: false, error: error.message, logs: results };
  }
}

Flujo del agente

// agent.ts
async function processRequest(userRequest: string) {
  // 1. El agente analiza la solicitud
  const plan = await llm.analyze(userRequest);

  // 2. Genera código para resolver la tarea
  const code = await llm.generateCode(`
    // Contexto disponible:
    // - gdrive: Google Drive API
    // - salesforce: Salesforce API

    // Tarea: ${plan.description}

    ${plan.suggestedApproach}
  `);

  // 3. Ejecuta el código en sandbox
  const result = await executeCode(code, {
    gdrive,
    salesforce,
    // Solo las herramientas necesarias
  });

  // 4. Devuelve resultado resumido
  return result.output;
}

Resultados de eficiencia

Los resultados son impresionantes:

MétricaEnfoque tradicionalCon ejecución de código
Tokens por tarea~100,000~1,300
Reducción-98.7%
LatenciaAltaBaja
PrivacidadBajaAlta

Ejemplo comparativo

Tarea: Actualizar Salesforce con notas de una reunión de Google Drive

Enfoque tradicional:

1. Cargar todas las herramientas (~50,000 tokens)
2. Llamar getDocument (~50,000 tokens de transcripción)
3. Pasar transcripción al modelo
4. Llamar updateRecord
Total: ~100,000+ tokens

Con ejecución de código:

const transcript = (await gdrive.getDocument({
  documentId: 'abc123'
})).content;

await salesforce.updateRecord({
  objectType: 'SalesMeeting',
  recordId: '00Q5f000001abcXYZ',
  data: { Notes: transcript }
});
Total: ~1,300 tokens (solo el código)

Consideraciones de seguridad

La ejecución de código introduce complejidad operativa. Es crítico implementar:

1. Sandboxing adecuado

const vm = new VM({
  timeout: 30000,  // Límite de tiempo
  sandbox: {},     // Contexto aislado
  eval: false,     // Sin eval dinámico
  wasm: false      // Sin WebAssembly
});

2. Límites de recursos

const resourceLimits = {
  maxMemory: 128 * 1024 * 1024, // 128MB
  maxCPU: 10000, // 10 segundos
  maxNetworkRequests: 100
};

3. Monitoreo y auditoría

// Logging de todas las ejecuciones
await auditLog.record({
  timestamp: new Date(),
  code: sanitize(code),
  result: result.success ? 'success' : 'failure',
  resourceUsage: {
    memory: process.memoryUsage(),
    duration: endTime - startTime
  }
});

4. Permisos granulares

const permissions = {
  'gdrive.read': true,
  'gdrive.write': false,
  'salesforce.read': true,
  'salesforce.write': true,
  'filesystem': false,
  'network.external': false
};

Casos de uso ideales

1. Procesamiento de documentos largos

// Extraer solo información relevante de documentos extensos
const doc = await gdrive.getDocument({ id });
const relevantSections = doc.content
  .split('##')
  .filter(section => section.includes(keyword));
return relevantSections;

2. Agregación de múltiples fuentes

// Combinar datos de múltiples APIs sin pasar todo al modelo
const [sales, inventory, orders] = await Promise.all([
  salesforce.query('SELECT * FROM Sales'),
  erp.getInventory(),
  orders.getPending()
]);

return {
  lowStockItems: inventory.filter(i => i.stock < 10),
  pendingOrdersValue: orders.reduce((sum, o) => sum + o.value, 0),
  topProducts: sales.slice(0, 5)
};

3. Transformación de datos complejos

// Transformaciones que serían costosas en el modelo
const rawData = await api.getData();
const transformed = rawData
  .map(normalizeFields)
  .filter(isValid)
  .reduce(aggregate, {});
return transformed;

Mejores prácticas

1. Cargar herramientas bajo demanda

// En lugar de cargar todo
const neededTools = plan.requiredTools;
const tools = await loadTools(neededTools);

2. Procesar datos localmente

// Filtrar antes de devolver al modelo
const filtered = data.filter(relevantCriteria);
const summarized = summarize(filtered);
return summarized;

3. Cachear resultados

const cache = new Map();

async function getCached(key, fetcher) {
  if (!cache.has(key)) {
    cache.set(key, await fetcher());
  }
  return cache.get(key);
}

4. Manejar errores graciosamente

try {
  const result = await executeCode(code, context);
  return result;
} catch (error) {
  // Devolver error útil sin exponer detalles internos
  return {
    success: false,
    error: 'Error procesando solicitud',
    suggestion: 'Intenta reformular la tarea'
  };
}

Conclusión

La ejecución de código con MCP representa un cambio de paradigma en cómo construimos agentes de IA:

  • Eficiencia radical: Reducción de hasta 98.7% en consumo de tokens
  • Mejor privacidad: Datos sensibles procesados localmente
  • Mayor flexibilidad: Agentes que escriben código adaptado a cada tarea
  • Escalabilidad: Menos carga en el modelo = más tareas procesables

Esta técnica es especialmente valiosa para:

  • Procesamiento de documentos largos
  • Integración con múltiples APIs
  • Tareas que requieren transformación de datos
  • Escenarios con datos sensibles

La complejidad adicional de implementar un entorno de ejecución seguro se compensa ampliamente con las mejoras en eficiencia y capacidad.


Post anterior: MCP Apps: Interfaces Interactivas

Recursos: