geoapify-mcp-server
Version:
Geoapify API MCP Server for location-based services - 一键部署的地理位置服务
203 lines (169 loc) • 5.55 kB
JavaScript
const axios = require('axios');
const { logger } = require('../utils/logger.js');
const { CacheManager } = require('../utils/cache.js');
class GeoapifyService {
constructor(apiKey) {
if (!apiKey) {
throw new Error('Geoapify API key is required');
}
this.apiKey = apiKey;
this.baseURL = 'https://api.geoapify.com';
this.cache = new CacheManager();
// 配置axios实例
this.client = axios.create({
baseURL: this.baseURL,
timeout: 30000,
headers: {
'User-Agent': 'Geoapify-MCP-Server/1.0.0'
}
});
// 请求拦截器
this.client.interceptors.request.use(
(config) => {
config.params = { ...config.params, apiKey: this.apiKey };
logger.debug(`API Request: ${config.method?.toUpperCase()} ${config.url}`, { params: config.params });
return config;
},
(error) => {
logger.error('Request interceptor error:', error);
return Promise.reject(error);
}
);
// 响应拦截器
this.client.interceptors.response.use(
(response) => {
logger.debug(`API Response: ${response.status}`, {
url: response.config.url,
dataSize: JSON.stringify(response.data).length
});
return response;
},
(error) => {
logger.error('API Error:', {
url: error.config?.url,
status: error.response?.status,
message: error.response?.data?.message || error.message
});
return Promise.reject(this.handleApiError(error));
}
);
}
handleApiError(error) {
if (error.response) {
const { status, data } = error.response;
switch (status) {
case 401:
return new Error('Invalid API key');
case 403:
return new Error('API key quota exceeded or access denied');
case 429:
return new Error('Rate limit exceeded');
case 400:
return new Error(`Bad request: ${data.message || 'Invalid parameters'}`);
default:
return new Error(`API error (${status}): ${data.message || 'Unknown error'}`);
}
}
return error;
}
async geocode(address, options = {}) {
const cacheKey = `geocode:${address}:${JSON.stringify(options)}`;
const cached = await this.cache.get(cacheKey);
if (cached) return cached;
const params = {
text: address,
format: 'json',
limit: 5,
...options
};
const response = await this.client.get('/v1/geocode/search', { params });
const result = response.data;
await this.cache.set(cacheKey, result, 3600); // 缓存1小时
return result;
}
async reverseGeocode(lat, lon, options = {}) {
const cacheKey = `reverse:${lat}:${lon}:${JSON.stringify(options)}`;
const cached = await this.cache.get(cacheKey);
if (cached) return cached;
const params = {
lat,
lon,
format: 'json',
...options
};
const response = await this.client.get('/v1/geocode/reverse', { params });
const result = response.data;
await this.cache.set(cacheKey, result, 3600);
return result;
}
async routing(waypoints, mode = 'drive', options = {}) {
const waypointsStr = Array.isArray(waypoints) ? waypoints.join('|') : waypoints;
const cacheKey = `routing:${waypointsStr}:${mode}:${JSON.stringify(options)}`;
const cached = await this.cache.get(cacheKey);
if (cached) return cached;
const params = {
waypoints: waypointsStr,
mode,
format: 'json',
...options
};
const response = await this.client.get('/v1/routing', { params });
const result = response.data;
await this.cache.set(cacheKey, result, 1800); // 缓存30分钟
return result;
}
async searchPlaces(categories, filter, options = {}) {
const cacheKey = `places:${categories}:${filter}:${JSON.stringify(options)}`;
const cached = await this.cache.get(cacheKey);
if (cached) return cached;
const params = {
categories,
filter,
limit: 20,
...options
};
const response = await this.client.get('/v2/places', { params });
const result = response.data;
await this.cache.set(cacheKey, result, 1800);
return result;
}
async autocomplete(text, options = {}) {
const cacheKey = `autocomplete:${text}:${JSON.stringify(options)}`;
const cached = await this.cache.get(cacheKey);
if (cached) return cached;
const params = {
text,
format: 'json',
limit: 10,
...options
};
const response = await this.client.get('/v1/geocode/autocomplete', { params });
const result = response.data;
await this.cache.set(cacheKey, result, 600); // 缓存10分钟
return result;
}
async isoline(lat, lon, options = {}) {
const params = {
lat,
lon,
format: 'json',
...options
};
const response = await this.client.get('/v1/isoline', { params });
return response.data;
}
async placeDetails(placeId, options = {}) {
const cacheKey = `place_details:${placeId}:${JSON.stringify(options)}`;
const cached = await this.cache.get(cacheKey);
if (cached) return cached;
const params = {
id: placeId,
...options
};
const response = await this.client.get('/v2/place-details', { params });
const result = response.data;
await this.cache.set(cacheKey, result, 7200); // 缓存2小时
return result;
}
}
module.exports = { GeoapifyService };