UNPKG

geoapify-mcp-server

Version:

Geoapify API MCP Server for location-based services - 一键部署的地理位置服务

300 lines (284 loc) 8.93 kB
const Joi = require('joi'); const { logger } = require('../utils/logger.js'); class MCPHandler { constructor(geoapifyService) { this.geoapifyService = geoapifyService; this.tools = this.defineTools(); } defineTools() { return [ { name: "geocode_address", description: "将地址转换为经纬度坐标,支持全球地址查询", inputSchema: { type: "object", properties: { address: { type: "string", description: "要编码的地址,支持结构化或自由格式地址" }, country: { type: "string", description: "国家过滤器,使用ISO 3166-1 alpha-2代码" }, language: { type: "string", description: "结果语言,使用ISO 639-1代码" }, limit: { type: "number", description: "返回结果数量限制,默认5", minimum: 1, maximum: 20 } }, required: ["address"] } }, { name: "reverse_geocode", description: "将经纬度坐标转换为地址信息", inputSchema: { type: "object", properties: { lat: { type: "number", description: "纬度", minimum: -90, maximum: 90 }, lon: { type: "number", description: "经度", minimum: -180, maximum: 180 }, language: { type: "string", description: "结果语言,使用ISO 639-1代码" } }, required: ["lat", "lon"] } }, { name: "calculate_route", description: "计算两点或多点间的最优路线", inputSchema: { type: "object", properties: { waypoints: { type: "array", description: "路径点数组,每个点为 'lat,lon' 格式", items: { type: "string" }, minItems: 2 }, mode: { type: "string", enum: ["drive", "walk", "bicycle", "truck", "motorcycle", "transit"], description: "交通方式", default: "drive" }, type: { type: "string", enum: ["balanced", "short", "less_maneuvers"], description: "路线优化类型", default: "balanced" }, avoid: { type: "string", description: "避开的道路类型,如 'tolls|ferries'" }, details: { type: "string", description: "额外详情,如 'instruction_details,elevation'" } }, required: ["waypoints"] } }, { name: "search_places", description: "搜索指定类别的地点或兴趣点", inputSchema: { type: "object", properties: { categories: { type: "string", description: "地点类别,如 'catering.restaurant,catering.cafe'" }, filter: { type: "string", description: "地理过滤器,支持circle:lon,lat,radius 或 rect:lon1,lat1,lon2,lat2" }, bias: { type: "string", description: "位置偏好,如 'proximity:lon,lat'" }, conditions: { type: "string", description: "额外条件,如 'wheelchair,internet_access'" }, limit: { type: "number", description: "结果数量限制,默认20", minimum: 1, maximum: 500 }, language: { type: "string", description: "结果语言" } }, required: ["categories"] } }, { name: "address_autocomplete", description: "地址自动补全,提供实时地址建议", inputSchema: { type: "object", properties: { text: { type: "string", description: "部分地址文本" }, country: { type: "string", description: "国家过滤器" }, limit: { type: "number", description: "建议数量限制,默认10", minimum: 1, maximum: 20 }, language: { type: "string", description: "结果语言" } }, required: ["text"] } }, { name: "calculate_isoline", description: "计算等时线或等距线,显示可达性区域", inputSchema: { type: "object", properties: { lat: { type: "number", description: "起点纬度" }, lon: { type: "number", description: "起点经度" }, type: { type: "string", enum: ["time", "distance"], description: "等线类型:时间或距离", default: "time" }, mode: { type: "string", enum: ["drive", "walk", "bicycle", "transit"], description: "交通方式", default: "drive" }, range: { type: "number", description: "范围值(秒或米)", minimum: 1 } }, required: ["lat", "lon", "range"] } }, { name: "get_place_details", description: "获取地点的详细信息", inputSchema: { type: "object", properties: { place_id: { type: "string", description: "地点ID,从搜索结果中获取" }, language: { type: "string", description: "结果语言" } }, required: ["place_id"] } } ]; } getTools() { return { tools: this.tools, server_info: { name: "Geoapify MCP Server", version: "1.0.0", description: "提供地理编码、路线规划、地点搜索等位置服务" } }; } async callTool(toolName, args) { const tool = this.tools.find(t => t.name === toolName); if (!tool) { throw new Error(`Unknown tool: ${toolName}`); } // 验证输入参数 const { error, value } = Joi.object(tool.inputSchema.properties).validate(args); if (error) { throw new Error(`Invalid arguments: ${error.details[0].message}`); } logger.info(`Executing tool: ${toolName}`, { args: value }); try { switch (toolName) { case 'geocode_address': return await this.geoapifyService.geocode(value.address, { filter: value.country ? `countrycode:${value.country}` : undefined, lang: value.language, limit: value.limit }); case 'reverse_geocode': return await this.geoapifyService.reverseGeocode(value.lat, value.lon, { lang: value.language }); case 'calculate_route': return await this.geoapifyService.routing(value.waypoints, value.mode, { type: value.type, avoid: value.avoid, details: value.details }); case 'search_places': return await this.geoapifyService.searchPlaces(value.categories, value.filter, { bias: value.bias, conditions: value.conditions, limit: value.limit, lang: value.language }); case 'address_autocomplete': return await this.geoapifyService.autocomplete(value.text, { filter: value.country ? `countrycode:${value.country}` : undefined, limit: value.limit, lang: value.language }); case 'calculate_isoline': return await this.geoapifyService.isoline(value.lat, value.lon, { type: value.type, mode: value.mode, range: value.range }); case 'get_place_details': return await this.geoapifyService.placeDetails(value.place_id, { lang: value.language }); default: throw new Error(`Tool implementation not found: ${toolName}`); } } catch (error) { logger.error(`Tool execution failed: ${toolName}`, { error: error.message, args: value }); throw error; } } } module.exports = { MCPHandler };