We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/arlexperalta/economia-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
ListResourcesRequestSchema,
ReadResourceRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";
// API base URL (usa variable de entorno o default a producción)
const API_BASE = process.env.ECONOMIA_API_URL || "https://economia.arlexperalta.com";
const API_KEY = process.env.ECONOMIA_API_KEY || "";
// Crear servidor MCP
const server = new Server(
{
name: "economia-venezuela",
version: "1.0.0",
},
{
capabilities: {
tools: {},
resources: {},
},
}
);
// Helper para llamar a la API
async function callApi<T>(endpoint: string, params?: Record<string, string>): Promise<T> {
const url = new URL(`${API_BASE}/api/v1${endpoint}`);
if (params) {
Object.entries(params).forEach(([key, value]) => {
if (value) url.searchParams.append(key, value);
});
}
const headers: Record<string, string> = {
"Accept": "application/json",
"User-Agent": "economia-mcp/1.0.0",
};
if (API_KEY) {
headers["X-API-Key"] = API_KEY;
}
const response = await fetch(url.toString(), { headers });
if (!response.ok) {
const error = await response.json().catch(() => ({}));
throw new Error(error.error?.message || `API error: ${response.status}`);
}
return response.json() as Promise<T>;
}
// Schemas de validación para los parámetros
const ExchangeRatesParamsSchema = z.object({
pairs: z.string().optional().describe("Pares de divisas separados por coma (ej: USD-VES,EUR-VES)"),
sources: z.string().optional().describe("Fuentes separadas por coma (ej: BCV,DolarToday)"),
});
const HistoryParamsSchema = z.object({
pair: z.string().default("USD-VES").describe("Par de divisas (ej: USD-VES)"),
days: z.number().min(1).max(90).default(30).describe("Días de historial (1-90)"),
});
const InflationParamsSchema = z.object({
period: z.enum(["monthly", "yearly", "all"]).default("monthly").describe("Tipo de período"),
year: z.string().optional().describe("Filtrar por año (ej: 2024)"),
});
const BasketParamsSchema = z.object({
category: z.enum(["food", "transport", "utilities", "all"]).default("all").describe("Categoría de productos"),
});
// Listar herramientas disponibles
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "get_exchange_rates",
description: `Obtiene las tasas de cambio actuales del Bolívar venezolano (VES) contra USD, EUR y otras divisas.
Fuentes disponibles:
- BCV: Tasa oficial del Banco Central de Venezuela
- DolarToday: Tasa del mercado paralelo
- MonitorDolar: Agregador de tasas paralelas
- Paralelo (Promedio): Promedio del mercado informal
Útil para: conversiones de moneda, análisis financiero, seguimiento de la economía venezolana.`,
inputSchema: {
type: "object",
properties: {
pairs: {
type: "string",
description: "Pares de divisas separados por coma (ej: USD-VES,EUR-VES). Por defecto retorna todos.",
},
sources: {
type: "string",
description: "Fuentes separadas por coma (ej: BCV,DolarToday). Por defecto retorna todas.",
},
},
},
},
{
name: "get_exchange_rate_history",
description: `Obtiene el historial de tasas de cambio para un par de divisas específico.
Incluye estadísticas: mínimo, máximo, promedio y cambio porcentual del período.
Útil para: análisis de tendencias, gráficos, predicciones.`,
inputSchema: {
type: "object",
properties: {
pair: {
type: "string",
description: "Par de divisas (ej: USD-VES, EUR-VES)",
default: "USD-VES",
},
days: {
type: "number",
description: "Número de días de historial (1-90)",
default: 30,
minimum: 1,
maximum: 90,
},
},
},
},
{
name: "get_inflation",
description: `Obtiene datos de inflación de Venezuela.
Incluye: tasa mensual, acumulado del año, tasa interanual.
Fuente: Banco Central de Venezuela (BCV).
Útil para: análisis económico, planificación financiera, contexto macroeconómico.`,
inputSchema: {
type: "object",
properties: {
period: {
type: "string",
enum: ["monthly", "yearly", "all"],
description: "Tipo de período: monthly (mensual), yearly (anual), all (todos)",
default: "monthly",
},
year: {
type: "string",
description: "Filtrar por año específico (ej: 2024)",
},
},
},
},
{
name: "get_basket_prices",
description: `Obtiene precios de la canasta básica venezolana en Bolívares y USD.
Categorías:
- food: Alimentos (harina, arroz, carne, etc.)
- transport: Transporte (metro, gasolina)
- utilities: Servicios (electricidad, agua, internet)
Incluye: precio individual, total de la canasta, tasa de cambio utilizada.
Útil para: costo de vida, comparaciones de precios, análisis de poder adquisitivo.`,
inputSchema: {
type: "object",
properties: {
category: {
type: "string",
enum: ["food", "transport", "utilities", "all"],
description: "Categoría de productos",
default: "all",
},
},
},
},
{
name: "get_sources",
description: "Lista las fuentes de datos disponibles con su frecuencia de actualización y pares soportados.",
inputSchema: {
type: "object",
properties: {},
},
},
],
}));
// Ejecutar herramientas
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case "get_exchange_rates": {
const params = ExchangeRatesParamsSchema.parse(args || {});
const data = await callApi<ApiResponse>("/exchange-rates", {
pairs: params.pairs || "",
sources: params.sources || "",
});
return {
content: [
{
type: "text",
text: formatExchangeRates(data),
},
],
};
}
case "get_exchange_rate_history": {
const params = HistoryParamsSchema.parse(args || {});
const data = await callApi<ApiResponse>("/exchange-rates/history", {
pair: params.pair,
days: params.days.toString(),
});
return {
content: [
{
type: "text",
text: formatHistory(data),
},
],
};
}
case "get_inflation": {
const params = InflationParamsSchema.parse(args || {});
const data = await callApi<ApiResponse>("/inflation", {
period: params.period,
year: params.year || "",
});
return {
content: [
{
type: "text",
text: formatInflation(data),
},
],
};
}
case "get_basket_prices": {
const params = BasketParamsSchema.parse(args || {});
const data = await callApi<ApiResponse>("/prices/basket", {
category: params.category,
});
return {
content: [
{
type: "text",
text: formatBasket(data),
},
],
};
}
case "get_sources": {
const data = await callApi<ApiResponse>("/exchange-rates/sources");
return {
content: [
{
type: "text",
text: formatSources(data),
},
],
};
}
default:
throw new Error(`Herramienta no encontrada: ${name}`);
}
} catch (error) {
const message = error instanceof Error ? error.message : "Error desconocido";
return {
content: [
{
type: "text",
text: `Error: ${message}`,
},
],
isError: true,
};
}
});
// Resources: información estática útil
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
resources: [
{
uri: "economia://info/about",
name: "Sobre la API",
description: "Información sobre la API de Economía Venezuela",
mimeType: "text/plain",
},
{
uri: "economia://info/sources",
name: "Fuentes de datos",
description: "Descripción de las fuentes de datos utilizadas",
mimeType: "text/plain",
},
],
}));
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
const { uri } = request.params;
if (uri === "economia://info/about") {
return {
contents: [
{
uri,
mimeType: "text/plain",
text: `API Economía Venezuela
Proporciona datos económicos de Venezuela en tiempo real:
- Tasas de cambio (oficial BCV y mercado paralelo)
- Historial de tasas hasta 90 días
- Datos de inflación mensual y anual
- Precios de canasta básica
Desarrollado por Peralta Digital
https://economia.arlexperalta.com`,
},
],
};
}
if (uri === "economia://info/sources") {
return {
contents: [
{
uri,
mimeType: "text/plain",
text: `Fuentes de Datos
BCV (Banco Central de Venezuela)
- Tipo: Oficial
- Actualización: Diaria
- Pares: USD-VES, EUR-VES
DolarToday
- Tipo: Mercado paralelo
- Actualización: Cada 6 horas
- Pares: USD-VES, EUR-VES, BTC-VES
Monitor Dólar
- Tipo: Agregador paralelo
- Actualización: Cada 6 horas
- Pares: USD-VES
Promedio Paralelo
- Tipo: Agregado de múltiples fuentes
- Actualización: Cada 6 horas
- Pares: USD-VES, EUR-VES`,
},
],
};
}
throw new Error(`Recurso no encontrado: ${uri}`);
});
// Tipos
interface ApiResponse {
success: boolean;
data: unknown;
meta?: unknown;
}
// Formateadores de respuesta
function formatExchangeRates(response: ApiResponse): string {
if (!response.success) return "No se pudieron obtener las tasas";
const rates = response.data as Array<{
pair: string;
rate: number;
source: string;
timestamp: string;
change24h?: number;
}>;
let output = "## Tasas de Cambio Venezuela\n\n";
// Agrupar por par
const byPair = new Map<string, typeof rates>();
for (const rate of rates) {
const existing = byPair.get(rate.pair) || [];
existing.push(rate);
byPair.set(rate.pair, existing);
}
for (const [pair, pairRates] of byPair) {
output += `### ${pair}\n`;
for (const rate of pairRates) {
const change = rate.change24h ? ` (${rate.change24h > 0 ? "+" : ""}${rate.change24h.toFixed(2)}%)` : "";
output += `- **${rate.source}**: Bs. ${rate.rate.toFixed(2)}${change}\n`;
}
output += "\n";
}
const meta = response.meta as { lastUpdated?: string };
if (meta?.lastUpdated) {
output += `_Última actualización: ${new Date(meta.lastUpdated).toLocaleString("es-VE")}_`;
}
return output;
}
function formatHistory(response: ApiResponse): string {
if (!response.success) return "No se pudo obtener el historial";
const data = response.data as {
pair: string;
history: Array<{ date: string; rate: number; source: string }>;
statistics: { min: number; max: number; avg: number; change: number };
};
let output = `## Historial ${data.pair}\n\n`;
output += `### Estadísticas\n`;
output += `- Mínimo: Bs. ${data.statistics.min.toFixed(2)}\n`;
output += `- Máximo: Bs. ${data.statistics.max.toFixed(2)}\n`;
output += `- Promedio: Bs. ${data.statistics.avg.toFixed(2)}\n`;
output += `- Cambio: ${data.statistics.change > 0 ? "+" : ""}${data.statistics.change.toFixed(2)}%\n\n`;
output += `### Últimos valores\n`;
const recent = data.history.slice(-10);
for (const item of recent) {
output += `- ${item.date}: Bs. ${item.rate.toFixed(2)}\n`;
}
return output;
}
function formatInflation(response: ApiResponse): string {
if (!response.success) return "No se pudieron obtener los datos de inflación";
const data = response.data as Array<{
period: string;
rate: number;
accumulated: number;
interannual: number;
source: string;
}>;
let output = "## Inflación Venezuela\n\n";
for (const item of data.slice(0, 12)) {
output += `### ${item.period}\n`;
output += `- Mensual: ${item.rate.toFixed(1)}%\n`;
output += `- Acumulado: ${item.accumulated.toFixed(1)}%\n`;
output += `- Interanual: ${item.interannual.toFixed(1)}%\n\n`;
}
return output;
}
function formatBasket(response: ApiResponse): string {
if (!response.success) return "No se pudieron obtener los precios";
const data = response.data as {
items: Array<{ name: string; category: string; priceVES: number; priceUSD: number }>;
total: { VES: number; USD: number };
exchangeRateUsed: number;
};
let output = "## Canasta Básica Venezuela\n\n";
output += `_Tasa de cambio utilizada: Bs. ${data.exchangeRateUsed.toFixed(2)}/USD_\n\n`;
// Agrupar por categoría
const byCategory = new Map<string, typeof data.items>();
for (const item of data.items) {
const existing = byCategory.get(item.category) || [];
existing.push(item);
byCategory.set(item.category, existing);
}
const categoryNames: Record<string, string> = {
food: "Alimentos",
transport: "Transporte",
utilities: "Servicios",
};
for (const [category, items] of byCategory) {
output += `### ${categoryNames[category] || category}\n`;
for (const item of items) {
output += `- ${item.name}: Bs. ${item.priceVES.toFixed(2)} ($${item.priceUSD.toFixed(2)})\n`;
}
output += "\n";
}
output += `### Total Canasta\n`;
output += `- **Bolívares**: Bs. ${data.total.VES.toFixed(2)}\n`;
output += `- **Dólares**: $${data.total.USD.toFixed(2)}\n`;
return output;
}
function formatSources(response: ApiResponse): string {
if (!response.success) return "No se pudieron obtener las fuentes";
const data = response.data as {
sources: Array<{
id: string;
name: string;
type: string;
updateFrequency: string;
pairs: string[];
}>;
};
let output = "## Fuentes de Datos\n\n";
for (const source of data.sources) {
output += `### ${source.name}\n`;
output += `- Tipo: ${source.type === "official" ? "Oficial" : source.type === "parallel" ? "Paralelo" : "Agregado"}\n`;
output += `- Actualización: ${source.updateFrequency}\n`;
output += `- Pares: ${source.pairs.join(", ")}\n\n`;
}
return output;
}
// Iniciar servidor
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Economia Venezuela MCP server running on stdio");
}
main().catch((error) => {
console.error("Error starting server:", error);
process.exit(1);
});