Code Mode y Ejecución Programática: El Futuro de MCP en Producción
Introducción
Dos innovaciones recientes están transformando cómo construimos agentes de IA con MCP: Cloudflare Code Mode y las técnicas avanzadas de tool use de Anthropic. Ambas comparten una premisa fundamental: los LLMs son excepcionales escribiendo código, pero limitados usando herramientas directamente.
En este artículo exploramos ambos enfoques, sus ventajas, y cómo implementarlos para construir agentes más eficientes y precisos en producción.
El problema fundamental
Por qué fallan las herramientas tradicionales
Los LLMs actuales enfrentan tres limitaciones críticas con tool-calling directo:
1. Sobrecarga de definiciones
Cargar todas las herramientas simultáneamente consume el contexto antes de hacer trabajo real:
// Problema: 100 herramientas = 72,000 tokens
const tools = await mcpClient.listTools();
// ¡95% del contexto usado antes de empezar!
Impacto: En un modelo con 200k de contexto, solo quedan ~10k tokens para trabajo real.
2. Procesado redundante de resultados intermedios
Cada llamada de herramienta requiere un nuevo pase de inferencia:
// 20 llamadas = 20 ciclos de inferencia
const data1 = await tool1(); // Pase 1
const data2 = await tool2(data1); // Pase 2
// ... 18 pases más
const final = await tool20(data19); // Pase 20
Impacto: Latencia multiplicada y tokens procesados innecesariamente.
3. Falta de experiencia de entrenamiento
Como señala Cloudflare: “Los LLMs han visto mucho código. No han visto muchas ‘llamadas de herramientas’.”
Los modelos tienen millones de ejemplos de código Python/TypeScript, pero pocos ejemplos reales de orquestar herramientas complejas.
Solución 1: Cloudflare Code Mode
Concepto: TypeScript en el Edge
Code Mode convierte servidores MCP en APIs de TypeScript ejecutables en V8 isolates de Cloudflare Workers.
// El modelo genera código TypeScript
import { getDocument } from "./servers/google-drive";
import { updateRecord } from "./servers/salesforce";
const doc = await getDocument({ documentId: "abc123" });
const actionItems = doc.content
.split('\n')
.filter(line => line.includes('ACCIÓN:'))
.join('\n');
await updateRecord({
objectType: "Meeting",
recordId: "xyz789",
data: { notes: actionItems }
});
Arquitectura técnica
1. Conversión automática de schemas
Los schemas MCP se transforman en TypeScript con documentación:
// Schema MCP
{
"name": "getDocument",
"inputSchema": {
"properties": {
"documentId": { "type": "string" }
}
}
}
// Se convierte en:
export async function getDocument(args: {
documentId: string;
}): Promise<DocumentResult> {
// Implementación...
}
2. Worker Loader API
Nueva API de Cloudflare para cargar código dinámicamente:
const worker = await env.CODE_MODE.createWorker({
code: generatedCode,
bindings: {
gdrive: env.GDRIVE_BINDING,
salesforce: env.SALESFORCE_BINDING
},
limits: {
cpuMs: 10000,
memoryMb: 128
}
});
const result = await worker.execute();
Ventajas:
- Inicio en milisegundos (V8 isolates vs containers)
- Aislamiento completo de seguridad
- Escalado global automático
3. Sistema de bindings seguros
Servidores MCP expuestos como bindings JavaScript:
// El código ve APIs seguras
const gdrive = {
getDocument: async (args) => { /* seguro */ }
};
// NO puede hacer:
fetch("https://evil.com"); // ❌ Bloqueado
process.env.API_KEY; // ❌ Aislado
require("fs"); // ❌ No disponible
Implementación práctica
Configuración en wrangler.toml
name = "my-code-mode-agent"
main = "src/index.ts"
compatibility_date = "2025-11-25"
[code_mode]
enabled = true
[[code_mode.mcp_servers]]
name = "gdrive"
type = "mcp"
command = "npx"
args = ["-y", "@modelcontextprotocol/server-gdrive"]
[code_mode.limits]
cpu_ms = 10000
memory_mb = 128
Worker completo
// src/index.ts
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const { prompt } = await request.json();
// 1. Generar código con Claude
const code = await generateTypeScript(prompt, env);
// 2. Ejecutar en sandbox
const worker = await env.CODE_MODE.createWorker({
code,
bindings: {
gdrive: env.GDRIVE_BINDING,
salesforce: env.SALESFORCE_BINDING
}
});
// 3. Obtener resultado
const result = await worker.execute();
return Response.json(result);
}
};
Solución 2: Programmatic Tool Calling de Anthropic
Concepto: Python orchestration
Anthropic permite que Claude escriba código Python que orquesta herramientas:
# Claude genera este código Python
import tools
# Obtener documento
doc = tools.google_drive.get_document(document_id="abc123")
# Procesar localmente (no pasa por el modelo)
action_items = [
line.replace("ACCIÓN:", "").strip()
for line in doc.content.split('\n')
if "ACCIÓN:" in line
]
# Actualizar Salesforce
tools.salesforce.update_record(
object_type="Meeting",
record_id="xyz789",
data={"notes": "\n".join(action_items)}
)
Tres técnicas complementarias
1. Tool Search Tool
Descubre herramientas bajo demanda en lugar de cargar todas:
# Antes: Cargar 100 herramientas = 72k tokens
all_tools = load_all_tools()
# Ahora: Buscar solo lo necesario = 8.7k tokens
relevant_tools = search_tools(query="google drive document")
# Encuentra solo: get_document, list_files, share_document
Reducción: 85% menos tokens (72k → 8.7k)
2. Programmatic Tool Calling
Orquesta múltiples llamadas en un solo bloque de código:
# 20 llamadas en un solo pase de inferencia
results = []
for item in items[:20]:
data = tools.process(item)
results.append(data)
# Sin esto: 20 pases de inferencia
# Con esto: 1 pase de inferencia
Reducción: Elimina 19 ciclos de inferencia en este caso
3. Tool Use Examples
Proporciona ejemplos concretos en las definiciones:
{
"name": "update_record",
"description": "Actualiza un registro en Salesforce",
"examples": [
{
"input": {
"object_type": "Contact",
"record_id": "003xx000004TmiQAAS",
"data": {"Email": "[email protected]"}
},
"description": "Actualizar email de contacto"
}
]
}
Mejora: Precisión de 72% → 90% en parámetros complejos
Implementación con Claude
Configuración básica
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-sonnet-4-5-20250929",
max_tokens=4096,
tools=[
{
"type": "function",
"function": {
"name": "get_document",
"description": "Obtiene documento de Google Drive",
"parameters": {
"type": "object",
"properties": {
"document_id": {"type": "string"}
}
}
}
}
],
# 🔥 Activar programmatic tool calling
tool_choice={"type": "programmatic"}
)
Ejecutar código generado
# Claude devuelve código Python en response.content
generated_code = response.content[0].text
# Ejecutar en sandbox
from RestrictedPython import compile_restricted
compiled = compile_restricted(generated_code, '<string>', 'exec')
sandbox = {
'tools': {
'gdrive': gdrive_client,
'salesforce': salesforce_client
}
}
exec(compiled, sandbox)
result = sandbox.get('result')
Comparación de enfoques
| Aspecto | Cloudflare Code Mode | Anthropic Programmatic |
|---|---|---|
| Lenguaje | TypeScript | Python |
| Ejecución | V8 isolates (edge) | Sandbox local/servidor |
| Escalado | Global automático | Manual |
| Latencia | Ultra-baja (edge) | Depende de ubicación |
| Seguridad | Bindings + isolates | RestrictedPython |
| Inicio | Milisegundos | Variable |
| Mejor para | Producción edge | Desarrollo rápido |
Resultados de eficiencia
Caso de estudio real
Tarea: Extraer insights de 20 documentos y actualizar CRM
Enfoque tradicional (tool calling directo)
1. Cargar 100 herramientas: 72k tokens
2. Por cada documento:
- Llamar get_document: 50k tokens
- Procesar en modelo: 50k tokens
- Actualizar CRM: +tokens
3. Total: ~72k + (20 × 100k) = 2,072k tokens
Con Code Mode / Programmatic
// Código generado: ~500 tokens
for (const docId of documentIds) {
const doc = await gdrive.getDocument({ id: docId });
// Procesar localmente (0 tokens)
const insights = extractInsights(doc.content);
await crm.update({ id: docId, insights });
}
// Total: ~500 tokens generación + ~1k resultado = 1.5k tokens
Reducción: 99.9% (2,072k → 1.5k tokens)
Benchmarks oficiales
Cloudflare Code Mode:
- Reducción de tokens: 95-98%
- Latencia: 70% más rápido (edge execution)
- Costo: 97% menor
Anthropic Programmatic:
- Reducción de tokens: 37% en promedio
- Precisión: +18 puntos (72% → 90%)
- Latencia: 19+ pases de inferencia eliminados
Implementación híbrida recomendada
Combinar ambos enfoques
// Worker de Cloudflare con Claude Programmatic
export default {
async fetch(request: Request, env: Env) {
const { task } = await request.json();
// 1. Usar Tool Search para encontrar herramientas
const relevantTools = await searchTools(task);
// 2. Claude genera código TypeScript
const code = await claude.generateCode({
task,
tools: relevantTools,
examples: getExamples(relevantTools)
});
// 3. Ejecutar en Code Mode
const worker = await env.CODE_MODE.createWorker({
code,
bindings: createBindings(relevantTools)
});
return Response.json(await worker.execute());
}
};
Beneficios combinados
- Tool Search: Solo carga herramientas relevantes (85% menos tokens)
- Code Generation: Orquestación eficiente (elimina pases intermedios)
- Examples: Uso correcto de herramientas (90% precisión)
- Code Mode: Ejecución edge segura (latencia ultra-baja)
Resultado: 99%+ reducción de tokens, 90%+ precisión, latencia edge global
Casos de uso ideales
1. Procesamiento masivo de documentos
// Procesar 100 documentos eficientemente
const results = await Promise.all(
documentIds.map(async (id) => {
const doc = await gdrive.getDocument({ id });
return {
id,
summary: doc.content.slice(0, 200),
actionItems: extractActions(doc.content),
wordCount: doc.content.split(' ').length
};
})
);
// Solo devuelve metadata agregada al modelo
return {
totalDocs: results.length,
totalActions: results.reduce((sum, r) => sum + r.actionItems.length, 0),
avgWords: results.reduce((sum, r) => sum + r.wordCount, 0) / results.length
};
2. Integración multi-API compleja
// Combinar datos de 5 APIs diferentes
const [sales, inventory, orders, customers, analytics] = await Promise.all([
salesforce.query('SELECT * FROM Sales WHERE Date = TODAY'),
erp.getInventory({ warehouse: 'main' }),
shopify.getOrders({ status: 'pending' }),
crm.getCustomers({ tier: 'premium' }),
analytics.getMetrics({ range: 'today' })
]);
// Procesar todo localmente
const insights = {
lowStock: inventory.filter(i => i.quantity < 10).length,
pendingValue: orders.reduce((sum, o) => sum + o.total, 0),
topCustomer: customers.sort((a, b) => b.ltv - a.ltv)[0].name,
conversionRate: analytics.visitors > 0
? (sales.length / analytics.visitors * 100).toFixed(2)
: 0
};
return insights; // Solo 200 bytes vs 500kb de datos raw
3. Pipelines de transformación de datos
# Pipeline complejo de datos
import tools
# Obtener datos raw
raw_data = tools.database.query("""
SELECT * FROM transactions
WHERE date >= DATE_SUB(NOW(), INTERVAL 30 DAY)
""")
# Transformar localmente (sin pasar por modelo)
processed = []
for row in raw_data:
if row['amount'] > 1000:
processed.append({
'date': row['date'].isoformat(),
'amount': row['amount'],
'category': categorize(row['description']),
'risk_score': calculate_risk(row)
})
# Agregar
summary = {
'total_high_value': len(processed),
'total_amount': sum(r['amount'] for r in processed),
'avg_risk': sum(r['risk_score'] for r in processed) / len(processed),
'high_risk_count': len([r for r in processed if r['risk_score'] > 0.7])
}
tools.slack.send_message(
channel='#finance',
text=f"High-value transactions summary: {summary}"
)
Seguridad en producción
1. Validación de código generado
import { parse } from "@typescript-eslint/parser";
function validateCode(code: string): boolean {
// Lista negra de patrones peligrosos
const forbidden = [
/process\.env/,
/require\(['"]fs['"]\)/,
/import.*['"]http['"]/,
/eval\(/,
/Function\(/,
/fetch\(/
];
for (const pattern of forbidden) {
if (pattern.test(code)) {
throw new Error(`Código prohibido: ${pattern}`);
}
}
// Validar sintaxis
try {
parse(code);
return true;
} catch (error) {
throw new Error(`Sintaxis inválida: ${error.message}`);
}
}
2. Límites de recursos
const LIMITS = {
// CPU
cpuMs: 10000, // 10 segundos máximo
// Memoria
memoryMb: 128,
// Red
maxMcpCalls: 50,
maxConcurrentCalls: 10,
// Tiempo total
totalTimeoutMs: 30000,
// Tamaño de datos
maxResponseBytes: 10 * 1024 * 1024 // 10MB
};
3. Auditoría completa
interface ExecutionLog {
timestamp: number;
userId: string;
prompt: string;
generatedCode: string;
executionTime: number;
tokensUsed: number;
mcpCallsMade: number;
success: boolean;
errors?: string[];
}
async function logExecution(log: ExecutionLog) {
await auditDB.insert('executions', {
...log,
codeHash: hash(log.generatedCode),
promptHash: hash(log.prompt)
});
// Alertas para patrones sospechosos
if (log.mcpCallsMade > 100) {
await alert.send(`Unusual activity: ${log.mcpCallsMade} calls`);
}
}
4. Rate limiting por usuario
class RateLimiter {
async check(userId: string): Promise<void> {
const key = `ratelimit:${userId}`;
const current = await redis.incr(key);
if (current === 1) {
await redis.expire(key, 60); // 1 minuto
}
if (current > 100) {
throw new Error('Rate limit exceeded');
}
}
}
Mejores prácticas
1. Descubrimiento progresivo
// No cargar todas las herramientas
❌ const tools = await loadAllTools(); // 72k tokens
// Buscar solo lo necesario
✅ const tools = await searchTools(userQuery); // 8.7k tokens
2. Procesamiento local de datos
// No devolver datos raw al modelo
❌ return await gdrive.getDocument({ id }); // 50k tokens
// Procesar y resumir localmente
✅ const doc = await gdrive.getDocument({ id });
return {
title: doc.metadata.title,
summary: doc.content.slice(0, 200),
actionCount: (doc.content.match(/ACCIÓN:/g) || []).length
}; // ~100 tokens
3. Usar ejemplos en definiciones
const tools = [{
name: "update_record",
description: "Actualiza registro en Salesforce",
// ✅ Incluir ejemplos concretos
examples: [{
input: {
objectType: "Contact",
recordId: "003xx000004TmiQ",
data: { Email: "[email protected]" }
},
description: "Actualizar email de un contacto"
}],
parameters: { /* schema */ }
}];
4. Orquestar en paralelo
// ❌ Secuencial: 5 pases de inferencia
const a = await tool1();
const b = await tool2();
const c = await tool3();
// ✅ Paralelo: 1 pase de inferencia
const [a, b, c] = await Promise.all([
tool1(),
tool2(),
tool3()
]);
Conclusión
Code Mode de Cloudflare y Programmatic Tool Calling de Anthropic representan el futuro de agentes de IA en producción:
Ventajas clave:
- Eficiencia extrema: 95-99% reducción en tokens
- Mejor rendimiento: Los LLMs escriben código mejor que usan herramientas
- Mayor precisión: 90%+ con ejemplos y código estructurado
- Seguridad robusta: Sandboxing con isolates o RestrictedPython
- Escalabilidad: Edge computing global con Cloudflare
Cuándo usar estos enfoques:
- ✅ Procesamiento de grandes volúmenes de datos
- ✅ Integración con múltiples APIs (5+)
- ✅ Pipelines de transformación complejos
- ✅ Requisitos estrictos de eficiencia/costo
- ✅ Aplicaciones en producción a escala
Cuándo usar tool calling tradicional:
- ✅ Tareas simples con 2-3 herramientas
- ✅ Prototipos rápidos
- ✅ Casos donde seguridad de sandbox no es crítica
- ✅ Latencia no es factor limitante
La combinación de ambas técnicas (Tool Search + Programmatic + Code Mode) ofrece lo mejor de ambos mundos: máxima eficiencia, precisión superior y ejecución edge global.
Con estas herramientas, construir agentes de IA que operan eficientemente en producción a escala ya no es un desafío técnico, sino una realidad alcanzable.
Post anterior: Ejecución de Código con MCP
Recursos: