openweather.ts•3.91 kB
import { BaseApiClient } from "../../core/base-api.js";
import { loadConfig } from "../../core/config.js";
import { ToolRegistration } from "../../core/types.js";
class OpenWeatherClient extends BaseApiClient {
private readonly apiKey: string;
constructor(apiKey: string) {
super("https://api.openweathermap.org");
this.apiKey = apiKey;
}
async currentWeatherByCity(location: string) {
return this.request("/data/2.5/weather", {
query: { q: location, appid: this.apiKey, units: "metric" },
});
}
async forecastByCity(location: string) {
return this.request("/data/2.5/forecast", {
query: { q: location, appid: this.apiKey, units: "metric" },
});
}
async geocodeCity(location: string) {
return this.request("/geo/1.0/direct", {
query: { q: location, limit: 1, appid: this.apiKey },
});
}
async alertsByLocation(location: string) {
const geo = await this.geocodeCity(location);
const first = Array.isArray(geo) && geo.length > 0 ? geo[0] : undefined;
if (!first) return { alerts: [] };
const { lat, lon } = first as { lat: number; lon: number };
// One Call 3.0 - alerts field appears if available in your plan
const data = await this.request("/data/3.0/onecall", {
query: { lat, lon, appid: this.apiKey, units: "metric" },
});
return (data as any).alerts || [];
}
}
export function registerOpenWeather(): ToolRegistration {
const cfg = loadConfig();
const client = new OpenWeatherClient(cfg.openWeatherApiKey || "");
return {
tools: [
{
name: "get_current_weather",
description: "Get current weather by city name",
inputSchema: {
type: "object",
properties: {
location: { type: "string", description: "City name, e.g., London" },
},
required: ["location"],
},
},
{
name: "get_weather_forecast",
description: "Get 5-day/3-hour forecast by city name",
inputSchema: {
type: "object",
properties: {
location: { type: "string" },
days: { type: "number", description: "Days to include (1-5)" },
},
required: ["location"],
},
},
{
name: "get_weather_alerts",
description: "Get weather alerts for a location (requires One Call support)",
inputSchema: {
type: "object",
properties: {
location: { type: "string" },
},
required: ["location"],
},
},
],
handlers: {
async get_current_weather(args: Record<string, unknown>) {
if (!cfg.openWeatherApiKey) throw new Error("OPENWEATHER_API_KEY is not configured");
const location = String(args.location || "");
if (!location) throw new Error("location is required");
return client.currentWeatherByCity(location);
},
async get_weather_forecast(args: Record<string, unknown>) {
if (!cfg.openWeatherApiKey) throw new Error("OPENWEATHER_API_KEY is not configured");
const location = String(args.location || "");
const days = args.days ? Number(args.days) : undefined;
if (!location) throw new Error("location is required");
const data: any = await client.forecastByCity(location);
if (!days) return data;
const count = Math.max(1, Math.min(5, days));
const list = (data as any).list || [];
return { ...(data as object), list: list.slice(0, count * 8) } as any;
},
async get_weather_alerts(args: Record<string, unknown>) {
if (!cfg.openWeatherApiKey) throw new Error("OPENWEATHER_API_KEY is not configured");
const location = String(args.location || "");
if (!location) throw new Error("location is required");
return client.alertsByLocation(location);
},
},
};
}