import axios, { AxiosResponse } from 'axios';
export interface IPLocationResponse {
ip: string;
city: string;
region: string;
country: string;
country_name: string;
continent_code: string;
latitude: number;
longitude: number;
timezone: string;
currency: string;
languages: string;
}
export interface DetectedLocation {
lat: number;
lon: number;
city: string;
region: string;
country: string;
source: 'ip' | 'user';
}
export class LocationService {
private readonly ipApiUrl = 'https://ipapi.co/json/';
async detectLocationFromIP(): Promise<DetectedLocation> {
try {
const response: AxiosResponse<IPLocationResponse> = await axios.get(
this.ipApiUrl,
{
timeout: 5000, // 5 second timeout
headers: {
'User-Agent': 'OpenWeatherMap-MCP-Server/1.0',
},
}
);
const data = response.data;
// Validate response
if (!data.latitude || !data.longitude) {
throw new Error('Invalid location data received from IP service');
}
return {
lat: data.latitude,
lon: data.longitude,
city: data.city || 'Unknown',
region: data.region || '',
country: data.country_name || data.country || '',
source: 'ip',
};
} catch (error) {
if (axios.isAxiosError(error)) {
if (error.code === 'ECONNABORTED') {
throw new Error(
'Location detection timed out. Please specify your location manually.'
);
}
if (error.response?.status === 429) {
throw new Error(
'Location service rate limit exceeded. Please specify your location manually.'
);
}
}
throw new Error(
`Failed to detect location automatically: ${
error instanceof Error ? error.message : 'Unknown error'
}. Please specify your location manually.`
);
}
}
formatLocationMessage(location: DetectedLocation): string {
const locationStr = location.region
? `${location.city}, ${location.region}, ${location.country}`
: `${location.city}, ${location.country}`;
return `š **Detected location:** ${locationStr} (approximate)\nš” *For more accurate weather, specify your exact location using city name, address, or coordinates.*`;
}
}