server-sse.js•11 kB
import express from 'express';
import cors from 'cors';
/**
* 🏕️ TreePod Financial - Servidor MCP con Server-Sent Events
* Implementa MCP sobre SSE como Claude Web espera
*/
const app = express();
const PORT = process.env.PORT || 3005;
// Middleware CORS específico para Claude Web
app.use(cors({
origin: ['https://claude.ai', 'https://*.claude.ai', '*'],
methods: ['GET', 'POST', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization', 'Accept', 'Cache-Control'],
credentials: true
}));
app.use(express.json());
// Logging detallado
app.use((req, res, next) => {
console.log(`📞 ${new Date().toISOString()} ${req.method} ${req.path}`);
console.log('Headers:', JSON.stringify(req.headers, null, 2));
if (req.body && Object.keys(req.body).length > 0) {
console.log('Body:', JSON.stringify(req.body, null, 2));
}
next();
});
// Datos de TreePod
const treepodData = {
financial: {
monthly: { revenue: 45000, expenses: 28000, profit: 17000, occupancy: 78 },
quarterly: { revenue: 135000, expenses: 84000, profit: 51000, occupancy: 75 },
yearly: { revenue: 540000, expenses: 336000, profit: 204000, occupancy: 73 }
},
rates: {
standard: { high: 180, medium: 140, low: 100 },
premium: { high: 250, medium: 200, low: 150 },
luxury: { high: 350, medium: 280, low: 220 }
},
occupancy: {
today: { occupied: 12, available: 3, total: 15, rate: 80 },
week: { occupied: 78, available: 27, total: 105, rate: 74 },
month: { occupied: 310, available: 155, total: 465, rate: 67 }
}
};
// Endpoint raíz - Server-Sent Events para MCP
app.get('/', (req, res) => {
console.log('🌊 Iniciando conexión SSE para MCP...');
// Configurar headers para SSE
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Cache-Control'
});
// Enviar evento de inicialización
const initEvent = {
jsonrpc: "2.0",
method: "initialize",
result: {
protocolVersion: "2024-11-05",
capabilities: {
tools: {}
},
serverInfo: {
name: "treepod-financial",
version: "1.0.0"
}
}
};
res.write(`data: ${JSON.stringify(initEvent)}\n\n`);
// Enviar lista de herramientas
const toolsEvent = {
jsonrpc: "2.0",
method: "tools/list",
result: {
tools: [
{
name: "test_connection",
description: "Prueba la conexión con TreePod Financial MCP via SSE",
inputSchema: {
type: "object",
properties: {
message: {
type: "string",
description: "Mensaje de prueba",
default: "Hello TreePod SSE!"
}
}
}
},
{
name: "analyze_finances",
description: "Analiza las finanzas actuales del negocio TreePod Glamping",
inputSchema: {
type: "object",
properties: {
period: {
type: "string",
enum: ["monthly", "quarterly", "yearly"],
description: "Período de análisis",
default: "monthly"
},
include_projections: {
type: "boolean",
description: "Incluir proyecciones futuras",
default: true
}
}
}
},
{
name: "calculate_rates",
description: "Calcula tarifas óptimas para TreePod según temporada y demanda",
inputSchema: {
type: "object",
properties: {
season: {
type: "string",
enum: ["high", "medium", "low"],
description: "Temporada (alta, media, baja)",
default: "medium"
},
pod_type: {
type: "string",
enum: ["standard", "premium", "luxury"],
description: "Tipo de TreePod",
default: "standard"
},
nights: {
type: "number",
minimum: 1,
description: "Número de noches",
default: 1
}
}
}
},
{
name: "check_occupancy",
description: "Verifica el estado actual de ocupación de los TreePods",
inputSchema: {
type: "object",
properties: {
date_range: {
type: "string",
enum: ["today", "week", "month"],
description: "Rango de fechas para consultar",
default: "today"
}
}
}
}
]
}
};
res.write(`data: ${JSON.stringify(toolsEvent)}\n\n`);
// Mantener conexión viva
const keepAlive = setInterval(() => {
res.write(`data: {"type": "ping", "timestamp": "${new Date().toISOString()}"}\n\n`);
}, 30000);
// Manejar desconexión
req.on('close', () => {
console.log('🔌 Cliente SSE desconectado');
clearInterval(keepAlive);
});
req.on('error', (err) => {
console.error('❌ Error en conexión SSE:', err);
clearInterval(keepAlive);
});
});
// Endpoint POST para ejecutar herramientas
app.post('/tools/call', (req, res) => {
console.log('🛠️ Ejecutando herramienta via POST:', req.body);
try {
const { name, arguments: args } = req.body;
let toolResult;
switch (name) {
case 'test_connection':
const message = args?.message || 'Hello TreePod SSE!';
toolResult = {
content: [
{
type: "text",
text: `🏕️ TreePod Financial MCP - ¡Conexión SSE exitosa!\n\nMensaje: ${message}\n\n✅ Servidor SSE funcionando correctamente\n🌊 Protocolo MCP sobre Server-Sent Events\n📊 4 herramientas financieras disponibles\n🌐 Conectado via Claude Web\n🔄 Streaming activo`
}
]
};
break;
case 'analyze_finances':
const period = args?.period || 'monthly';
const includeProjections = args?.include_projections !== false;
const data = treepodData.financial[period];
let analysisText = `📊 ANÁLISIS FINANCIERO TREEPOD GLAMPING (${period.toUpperCase()})\n\n`;
analysisText += `💰 Ingresos: $${data.revenue.toLocaleString()}\n`;
analysisText += `💸 Gastos: $${data.expenses.toLocaleString()}\n`;
analysisText += `📈 Ganancia: $${data.profit.toLocaleString()}\n`;
analysisText += `🏕️ Ocupación: ${data.occupancy}%\n`;
if (includeProjections) {
const projection = Math.round(data.revenue * 1.12);
analysisText += `\n🔮 PROYECCIÓN:\n`;
analysisText += `📊 Próximo período: $${projection.toLocaleString()}\n`;
analysisText += `📈 Crecimiento estimado: 12%`;
}
toolResult = {
content: [{ type: "text", text: analysisText }]
};
break;
case 'calculate_rates':
const season = args?.season || 'medium';
const podType = args?.pod_type || 'standard';
const nights = args?.nights || 1;
const rate = treepodData.rates[podType][season];
const total = rate * nights;
const discount = nights >= 7 ? 0.15 : nights >= 3 ? 0.1 : 0;
const finalTotal = Math.round(total * (1 - discount));
let ratesText = `💰 CALCULADORA DE TARIFAS TREEPOD\n\n`;
ratesText += `🏕️ Tipo: ${podType.toUpperCase()}\n`;
ratesText += `📅 Temporada: ${season.toUpperCase()}\n`;
ratesText += `🌙 Noches: ${nights}\n\n`;
ratesText += `💵 Tarifa base: $${rate}/noche\n`;
ratesText += `💰 Subtotal: $${total}\n`;
ratesText += `🎯 Descuento: ${(discount * 100)}%\n`;
ratesText += `✅ TOTAL: $${finalTotal}\n\n`;
ratesText += discount > 0 ? '🎉 ¡Descuento aplicado por estadía extendida!' : '💡 Tip: 3+ noches = 10% descuento, 7+ noches = 15% descuento';
toolResult = {
content: [{ type: "text", text: ratesText }]
};
break;
case 'check_occupancy':
const dateRange = args?.date_range || 'today';
const occupancyData = treepodData.occupancy[dateRange];
let occupancyText = `🏕️ ESTADO DE OCUPACIÓN TREEPOD (${dateRange.toUpperCase()})\n\n`;
occupancyText += `✅ Ocupados: ${occupancyData.occupied} pods\n`;
occupancyText += `🟢 Disponibles: ${occupancyData.available} pods\n`;
occupancyText += `📊 Total: ${occupancyData.total} pods\n`;
occupancyText += `📈 Tasa ocupación: ${occupancyData.rate}%\n\n`;
occupancyText += occupancyData.rate >= 80 ? '🔥 ¡Excelente ocupación!' :
occupancyData.rate >= 60 ? '👍 Buena ocupación' :
'⚠️ Ocupación baja - considerar promociones';
toolResult = {
content: [{ type: "text", text: occupancyText }]
};
break;
default:
throw new Error(`Herramienta desconocida: ${name}`);
}
res.json({
jsonrpc: "2.0",
result: toolResult
});
} catch (error) {
console.error('❌ Error ejecutando herramienta:', error);
res.status(500).json({
jsonrpc: "2.0",
error: {
code: -32603,
message: "Error interno del servidor",
data: error.message
}
});
}
});
// Endpoint de información
app.get('/info', (req, res) => {
res.json({
name: "TreePod Financial MCP Server (SSE)",
description: "Agente financiero para TreePod Glamping - Compatible con Claude Web via SSE",
version: "1.0.0",
protocol: "MCP over Server-Sent Events",
capabilities: {
tools: true,
streaming: true,
sse: true
},
endpoints: {
sse: "/",
tools: "/tools/call",
info: "/info"
}
});
});
// Manejar preflight CORS
app.options('*', (req, res) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Accept, Cache-Control');
res.sendStatus(200);
});
app.listen(PORT, () => {
console.log(`🏕️ TreePod Financial MCP Server (SSE) running on http://localhost:${PORT}`);
console.log(`🌊 SSE endpoint: http://localhost:${PORT}/`);
console.log(`🛠️ Tools endpoint: http://localhost:${PORT}/tools/call`);
console.log(`📊 Info endpoint: http://localhost:${PORT}/info`);
console.log(`🌐 Compatible con Claude Web - Server-Sent Events`);
console.log(`📋 Herramientas: test_connection, analyze_finances, calculate_rates, check_occupancy`);
console.log(`🔄 Streaming MCP protocol activo`);
});