main.ts•6.21 kB
// main.ts (Servidor MCP de Clima y Historial - Versión Final)
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from "zod";
import { writeFile, readFile } from 'fs/promises'; // Importamos para manejo de archivos
// --- CONFIGURACIÓN DEL SERVIDOR ---
const server = new McpServer({
name: "Weather Server",
version: "1.0.0"
});
// --- HERRAMIENTA 1: OBTENER CLIMA (Con Historial) ---
server.tool(
'get-weather',
'Tool to get the current weather, temperature, and humidity for a specified city.',
{
city: z.string().describe("The name of the city to get the weather for")
},
async({ city }) => {
try {
// Paso 1: Obtener coordenadas de la ciudad (Geocoding API)
const geoResponse = await fetch(
`https://geocoding-api.open-meteo.com/v1/search?name=${city}&count=1&language=en&format=json`
);
const geoData = await geoResponse.json();
// Manejar ciudad no encontrada
if (!geoData.results || geoData.results.length === 0) {
return {
content: [{
type: "text",
text: `Sorry, I couldn't find a city named "${city}". Please check the spelling and try again.`
}]
};
}
// Paso 2: Obtener datos del clima usando coordenadas
const { latitude, longitude } = geoData.results[0];
const weatherUrl = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}¤t=temperature_2m,relative_humidity_2m,apparent_temperature,precipitation,weather_code&hourly=temperature_2m,precipitation&forecast_days=1`;
const weatherResponse = await fetch(weatherUrl);
const weatherData = await weatherResponse.json();
// Paso 3: Guardar en el historial
const logEntry = {
city,
latitude,
longitude,
timestamp: new Date().toISOString(),
temp: weatherData.current.temperature_2m
};
await writeFile('history.json', JSON.stringify(logEntry) + '\n', { flag: 'a' });
// Paso 4: Simplificar y devolver los datos clave
const simplifiedData = {
city_searched: city,
current_temperature_c: weatherData.current.temperature_2m,
relative_humidity_percent: weatherData.current.relative_humidity_2m,
weather_code: weatherData.current.weather_code,
};
return {
content: [{
type: "text",
text: JSON.stringify(simplifiedData, null, 2)
}]
};
} catch (error) {
let errorMessage = "An unknown error occurred.";
if (error instanceof Error) {
errorMessage = error.message;
} else if (typeof error === 'string') {
errorMessage = error;
}
return {
content: [{
type: "text",
text: `Error fetching weather data: ${errorMessage}`
}]
};
}
}
);
// --- HERRAMIENTA 2: OBTENER HISTORIAL ---
server.tool(
'get-history',
'Tool to retrieve the last 5 cities queried for weather information, useful for context.',
{},
async() => {
try {
// Leer el contenido del archivo history.json
const data = await readFile('history.json', 'utf-8');
// Procesar y obtener las últimas 5 entradas
const lines = data.trim().split('\n').filter(line => line.length > 0);
const recentHistory = lines.slice(-5);
const historyString = recentHistory.join('\n');
return {
content: [{
type: "text",
text: `Recent weather query history:\n${historyString}`
}]
};
} catch (error) {
// Manejar si el archivo aún no existe (ENOENT)
if (error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT') {
return {
content: [{
type: "text",
text: "No previous weather query history found yet. Ask for the weather in a city first to populate the history."
}]
};
}
let errorMessage = "An error occurred reading the history file.";
if (error instanceof Error) {
errorMessage = error.message;
}
return {
content: [{
type: "text",
text: `Error reading history: ${errorMessage}`
}]
};
}
}
);
// --- RECURSO: CÓDIGOS DE CLIMA (Corrección final) ---
server.resource(
'weather-codes', // 1. name: string
async () => { // 2. readCallback: Función que lee y devuelve el contenido
try {
// Leemos el contenido del archivo weather-codes.json
const content = await readFile('weather-codes.json', 'utf-8');
return {
description: 'WMO weather codes mapping for text descriptions.',
content: [{
type: 'text',
text: content // Devolvemos el contenido leído
}]
};
} catch (error) {
// Devolvemos error si no se encuentra el archivo
return {
description: 'Error loading WMO codes.',
content: [{
type: 'text',
text: `Error: weather-codes.json could not be loaded.`
}]
};
}
}
);
// --- CONFIGURACIÓN DE COMUNICACIÓN ---
const transport = new StdioServerTransport();
server.connect(transport);