Sfoglia il codice sorgente

feat: add baidu map mcp server (#246)

zijiren 6 mesi fa
parent
commit
12d3ad2658

+ 1 - 2
core/mcpproxy/stateless-streamable.go

@@ -1,7 +1,6 @@
 package mcpproxy
 
 import (
-	"encoding/json"
 	"fmt"
 	"io"
 	"net/http"
@@ -71,7 +70,7 @@ func (s *StreamableHTTPServer) handlePost(w http.ResponseWriter, r *http.Request
 	var baseMessage struct {
 		Method mcp.MCPMethod `json:"method"`
 	}
-	if err := json.Unmarshal(rawData, &baseMessage); err != nil {
+	if err := sonic.Unmarshal(rawData, &baseMessage); err != nil {
 		s.writeJSONRPCError(w, nil, mcp.PARSE_ERROR, "request body is not valid json")
 		return
 	}

+ 2 - 2
mcp-servers/12306/handlers.go

@@ -2,11 +2,11 @@ package train12306
 
 import (
 	"context"
-	"encoding/json"
 	"errors"
 	"fmt"
 	"net/url"
 
+	"github.com/bytedance/sonic"
 	"github.com/mark3labs/mcp-go/mcp"
 )
 
@@ -411,7 +411,7 @@ func (s *Server) handleGetTrainRouteStations(
 		return mcp.NewToolResultText("未查询到相关车次信息。"), nil
 	}
 
-	result, err := json.Marshal(routeStationsInfo)
+	result, err := sonic.Marshal(routeStationsInfo)
 	if err != nil {
 		return nil, fmt.Errorf("failed to marshal response: %w", err)
 	}

+ 6 - 5
mcp-servers/12306/parsers.go

@@ -1,13 +1,14 @@
 package train12306
 
 import (
-	"encoding/json"
 	"fmt"
 	"regexp"
 	"sort"
 	"strconv"
 	"strings"
 	"time"
+
+	"github.com/bytedance/sonic"
 )
 
 // extractPrices extracts price information from ticket data
@@ -373,13 +374,13 @@ func (s *Server) parseRouteStationsData(rawData []any) []RouteStationData {
 	result := make([]RouteStationData, 0, len(rawData))
 
 	for _, item := range rawData {
-		dataBytes, err := json.Marshal(item)
+		dataBytes, err := sonic.Marshal(item)
 		if err != nil {
 			continue
 		}
 
 		var routeStation RouteStationData
-		if err := json.Unmarshal(dataBytes, &routeStation); err != nil {
+		if err := sonic.Unmarshal(dataBytes, &routeStation); err != nil {
 			continue
 		}
 
@@ -421,13 +422,13 @@ func (s *Server) parseInterlineData(rawData []any) []InterlineData {
 	result := make([]InterlineData, 0, len(rawData))
 
 	for _, item := range rawData {
-		dataBytes, err := json.Marshal(item)
+		dataBytes, err := sonic.Marshal(item)
 		if err != nil {
 			continue
 		}
 
 		var interlineData InterlineData
-		if err := json.Unmarshal(dataBytes, &interlineData); err != nil {
+		if err := sonic.Unmarshal(dataBytes, &interlineData); err != nil {
 			continue
 		}
 

+ 5 - 5
mcp-servers/12306/tools.go

@@ -2,12 +2,12 @@ package train12306
 
 import (
 	"context"
-	"encoding/json"
 	"errors"
 	"fmt"
 	"strings"
 	"time"
 
+	"github.com/bytedance/sonic"
 	"github.com/mark3labs/mcp-go/mcp"
 )
 
@@ -67,7 +67,7 @@ func (s *Server) addGetStationsCodeInCityTool() {
 				return mcp.NewToolResultText("Error: City not found."), nil
 			}
 
-			result, err := json.Marshal(stations)
+			result, err := sonic.Marshal(stations)
 			if err != nil {
 				return nil, fmt.Errorf("failed to marshal response: %w", err)
 			}
@@ -111,7 +111,7 @@ func (s *Server) addGetStationCodeOfCitysTool() {
 				}
 			}
 
-			response, err := json.Marshal(result)
+			response, err := sonic.Marshal(result)
 			if err != nil {
 				return nil, fmt.Errorf("failed to marshal response: %w", err)
 			}
@@ -157,7 +157,7 @@ func (s *Server) addGetStationCodeByNamesTool() {
 				}
 			}
 
-			response, err := json.Marshal(result)
+			response, err := sonic.Marshal(result)
 			if err != nil {
 				return nil, fmt.Errorf("failed to marshal response: %w", err)
 			}
@@ -197,7 +197,7 @@ func (s *Server) addGetStationByTelecodeTool() {
 				return mcp.NewToolResultText("Error: Station not found."), nil
 			}
 
-			result, err := json.Marshal(station)
+			result, err := sonic.Marshal(station)
 			if err != nil {
 				return nil, fmt.Errorf("failed to marshal response: %w", err)
 			}

+ 3 - 2
mcp-servers/12306/utils.go

@@ -2,7 +2,6 @@ package train12306
 
 import (
 	"context"
-	"encoding/json"
 	"errors"
 	"fmt"
 	"io"
@@ -12,6 +11,8 @@ import (
 	"strconv"
 	"strings"
 	"time"
+
+	"github.com/bytedance/sonic"
 )
 
 // checkDate checks if the date is not earlier than today
@@ -98,7 +99,7 @@ func (s *Server) make12306Request(
 		return err
 	}
 
-	return json.Unmarshal(body, result)
+	return sonic.Unmarshal(body, result)
 }
 
 var (

+ 88 - 0
mcp-servers/baidu-map/README.md

@@ -0,0 +1,88 @@
+# 百度地图 MCP Server
+
+> <https://github.com/baidu-maps/mcp>
+
+## 概述
+
+百度地图API现已全面兼容[MCP协议](https://modelcontextprotocol.io/),是国内首家兼容MCP协议的地图服务商。
+
+百度地图提供的MCP Server,包含10个符合MCP协议标准的API接口,涵盖逆地理编码、地点检索、路线规划等。
+
+依赖`MCP Python SDK`和`MCP Typescript SDK`开发,任意支持MCP协议的智能体助手(如`Claude`、`Cursor`以及`千帆AppBuilder`等)都可以快速接入。
+
+**强烈推荐通过[SSE](https://lbsyun.baidu.com/faq/api?title=mcpserver/quickstart)接入百度地图MCP Server, 以获得更低的延迟和更高的稳定性。请不要忘记在[控制台](https://lbsyun.baidu.com/apiconsole/key)为你的AK勾选上`MCP(SSE)`服务。**
+
+## 工具
+
+1. 地理编码 `map_geocode`
+    - 描述: 将地址解析为对应的位置坐标, 地址结构越完整, 地址内容越准确, 解析的坐标精度越高
+    - 参数: `address` 地址信息
+    - 输出: `location` 纬经度坐标
+  
+2. 逆地理编码 `map_reverse_geocode`
+    - 描述: 根据纬经度坐标, 获取对应位置的地址描述, 所在行政区划, 道路以及相关POI等信息
+    - 参数:
+      - `latitude` 纬度坐标
+      - `longitude`经度坐标
+    - 输出: `formatted_address`, `uid`, `addressComponent` 等语义化地址信息
+
+3. 地点检索 `map_search_places`
+    - 描述: 支持检索城市内的地点信息(最小到`city`级别), 也可支持圆形区域内的周边地点信息检索
+    - 参数:
+      - `query` 检索关键词, 可用名称或类型, 多关键字使用英文逗号隔开, 如: `query=天安门,美食`
+      - `tag` 检索的类型偏好, 格式为`tag=美食`或者`tag=美食,酒店`
+      - `region` 检索的行政区划, 格式为`region=cityname`或`region=citycode`
+      - `location` 圆形检索中心点纬经度坐标, 格式为`location=lat,lng`
+      - `radius` 圆形检索的半径
+    - 输出: POI列表, 包含`name`, `location`, `address`等
+
+4. 地点详情检索 `map_place_details`
+    - 描述: 根据POI的uid,检索其相关的详情信息, 如评分、营业时间等(不同类型POI对应不同类别详情数据)
+    - 参数: `uid`POI的唯一标识
+    - 输出: POI详情, 包含`name`, `location`, `address`, `brand`, `price`等
+  
+5. 批量算路 `map_directions_matrix`
+    - 描述: 根据起点和终点坐标计算路线规划距离和行驶时间,支持驾车、骑行、步行。步行时任意起终点之间的距离不得超过200KM,驾车批量算路一次最多计算100条路线,起终点个数之积不能超过100。
+    - 参数:
+      - `origins` 起点纬经度列表, 格式为`origins=lat,lng`,多个起点用`|`分隔
+      - `destinations` 终点纬经度列表, 格式为`destinations=lat,lng`,多个终点用`|`分隔
+      - `model` 算路类型,可选取值包括 `driving`, `walking`, `riding`,默认使用`driving`
+    - 输出: 每条路线的耗时和距离, 包含`distance`, `duration`等
+
+6. 路线规划 `map_directions`
+    - 描述: 根据起终点位置名称或经纬度坐标规划出行路线和耗时, 可指定驾车、步行、骑行、公交等出行方式
+    - 参数:
+      - `origin` 起点位置名称或纬经度, 格式为`origin=lat,lng`
+      - `destination` 终点位置名称或纬经度, 格式为`destination=lat,lng`
+      - `model` 出行类型, 可选取值包括 `driving`, `walking`, `riding`, `transit`, 默认使用`driving`
+    - 输出: 路线详情,包含`steps`, `distance`, `duration`等
+  
+7. 天气查询 `map_weather`
+    - 描述: 通过行政区划或是经纬度坐标查询实时天气信息及未来5天天气预报
+    - 参数:
+      - `district_id` 行政区划编码
+      - `location` 经纬度坐标, 格式为`location=lng, lat`
+    - 输出: 天气信息, 包含`temperature`, `weather`, `wind`等
+
+8. IP定位 `map_ip_location`
+    - 描述: 通过所给IP获取具体位置信息和城市名称, 可用于定位IP或用户当前位置。可选参数`ip`,如果为空则获取本机IP地址(支持IPv4和IPv6)。
+    - 参数:
+      - `ip`(可选)需要定位的IP地址
+    - 输出: 当前所在城市和城市中点`location`
+
+9. 实时路况查询 `map_road_traffic`
+    - 描述: 查询实时交通拥堵情况, 可通过指定道路名和区域形状(矩形, 多边形, 圆形)进行实时路况查询。
+    - 参数:
+      - `model` 路况查询类型 (可选值包括`road`, `bound`, `polygon`, `around`, 默认使用`road`)
+      - `road_name` 道路名称和道路方向, `model=road`时必传 (如:`朝阳路南向北`)
+      - `city` 城市名称或城市adcode, `model=road`时必传 (如:`北京市`)
+      - `bounds` 区域左下角和右上角的纬经度坐标, `model=bound`时必传 (如:`39.9,116.4;39.9,116.4`)
+      - `vertexes` 多边形区域的顶点纬经度坐标, `model=polygon`时必传 (如:`39.9,116.4;39.9,116.4;39.9,116.4;39.9,116.4`)
+      - `center` 圆形区域的中心点纬经度坐标, `model=around`时必传 (如:`39.912078,116.464303`)
+      - `radius` 圆形区域的半径(米), 取值`[1,1000]`, `model=around`时必传 (如:`200`)
+    - 输出: 路况信息, 包含`road_name`, `traffic_condition`等
+
+10. POI智能提取 `map_poi_extract`
+    - 描述: 当所给的`API_KEY`带有**高级权限**才可使用, 根据所给文本内容提取其中的相关POI信息。
+    - 参数: `text_content` 用于提取POI的文本描述信息 (完整的旅游路线,行程规划,景点推荐描述等文本内容, 例如: 新疆独库公路和塔里木湖太美了, 从独山子大峡谷到天山神秘大峡谷也是很不错的体验)
+    - 输出:相关的POI信息,包含`name`, `location`等

+ 29 - 0
mcp-servers/baidu-map/init.go

@@ -0,0 +1,29 @@
+package baidumap
+
+import (
+	_ "embed"
+
+	mcpservers "github.com/labring/aiproxy/mcp-servers"
+)
+
+//go:embed README.md
+var readme string
+
+func init() {
+	mcpservers.Register(
+		mcpservers.NewMcp(
+			"baidu-map",
+			"Baidu Map",
+			mcpservers.McpTypeEmbed,
+			mcpservers.WithNewServerFunc(NewServer),
+			mcpservers.WithGitHubURL(
+				"https://github.com/baidu-maps/mcp",
+			),
+			mcpservers.WithConfigTemplates(configTemplates),
+			mcpservers.WithTags(
+				[]string{"baidu", "map", "geocoding", "search", "weather", "traffic"},
+			),
+			mcpservers.WithReadme(readme),
+		),
+	)
+}

+ 297 - 0
mcp-servers/baidu-map/modle.go

@@ -0,0 +1,297 @@
+package baidumap
+
+// Response 百度地图API基础响应结构
+type Response struct {
+	Status  int    `json:"status"`
+	Msg     string `json:"msg,omitempty"`
+	Message string `json:"message,omitempty"`
+}
+
+// GeocodeResponse 地理编码响应
+type GeocodeResponse struct {
+	Response
+	Result struct {
+		Location struct {
+			Lat float64 `json:"lat"`
+			Lng float64 `json:"lng"`
+		} `json:"location"`
+		Precise       int    `json:"precise"`
+		Confidence    int    `json:"confidence"`
+		Comprehension int    `json:"comprehension"`
+		Level         string `json:"level"`
+	} `json:"result"`
+}
+
+// ReverseGeocodeResponse 逆地理编码响应
+type ReverseGeocodeResponse struct {
+	Response
+	Result struct {
+		Location struct {
+			Lng float64 `json:"lng"`
+			Lat float64 `json:"lat"`
+		} `json:"location"`
+		FormattedAddress    string `json:"formatted_address"`
+		FormattedAddressPOI string `json:"formatted_address_poi"`
+		Business            string `json:"business"`
+		BusinessInfo        []struct {
+			Name     string `json:"name"`
+			Location struct {
+				Lng float64 `json:"lng"`
+				Lat float64 `json:"lat"`
+			} `json:"location"`
+			Adcode    int     `json:"adcode"`
+			Distance  float64 `json:"distance"`
+			Direction string  `json:"direction"`
+		} `json:"business_info"`
+		AddressComponent struct {
+			Country         string `json:"country"`
+			CountryCode     int    `json:"country_code"`
+			CountryCodeISO  string `json:"country_code_iso"`
+			CountryCodeISO2 string `json:"country_code_iso2"`
+			Province        string `json:"province"`
+			City            string `json:"city"`
+			CityLevel       int    `json:"city_level"`
+			District        string `json:"district"`
+			Town            string `json:"town"`
+			TownCode        string `json:"town_code"`
+			Distance        string `json:"distance"`
+			Direction       string `json:"direction"`
+			Adcode          string `json:"adcode"`
+			Street          string `json:"street"`
+			StreetNumber    string `json:"street_number"`
+		} `json:"addressComponent"`
+		EDZ struct {
+			Name string `json:"name"`
+		} `json:"edz"`
+		POIs               []any  `json:"pois"`
+		Roads              []any  `json:"roads"`
+		POIRegions         []any  `json:"poiRegions"`
+		SematicDescription string `json:"sematic_description"`
+		CityCode           int    `json:"cityCode"`
+	} `json:"result"`
+}
+
+// PlacesSearchResponse 地点搜索响应
+type PlacesSearchResponse struct {
+	Response
+	ResultType string `json:"result_type,omitempty"`
+	QueryType  string `json:"query_type,omitempty"`
+	Results    []struct {
+		Name     string `json:"name"`
+		Location struct {
+			Lat float64 `json:"lat"`
+			Lng float64 `json:"lng"`
+		} `json:"location"`
+		Address   string `json:"address"`
+		Province  string `json:"province"`
+		City      string `json:"city"`
+		Area      string `json:"area"`
+		StreetID  string `json:"street_id,omitempty"`
+		Telephone string `json:"telephone,omitempty"`
+		Detail    int    `json:"detail"`
+		UID       string `json:"uid"`
+	} `json:"results,omitempty"`
+	Result []struct {
+		Name     string `json:"name"`
+		Location struct {
+			Lat float64 `json:"lat"`
+			Lng float64 `json:"lng"`
+		} `json:"location"`
+		Address   string `json:"address"`
+		Province  string `json:"province"`
+		City      string `json:"city"`
+		Area      string `json:"area"`
+		StreetID  string `json:"street_id,omitempty"`
+		Telephone string `json:"telephone,omitempty"`
+		Detail    int    `json:"detail"`
+		UID       string `json:"uid"`
+	} `json:"result,omitempty"`
+}
+
+// PlaceDetailsResponse 地点详情响应
+type PlaceDetailsResponse struct {
+	Response
+	Result struct {
+		UID      string `json:"uid"`
+		StreetID string `json:"street_id"`
+		Name     string `json:"name"`
+		Location struct {
+			Lng float64 `json:"lng"`
+			Lat float64 `json:"lat"`
+		} `json:"location"`
+		Address    string `json:"address"`
+		Province   string `json:"province"`
+		City       string `json:"city"`
+		Area       string `json:"area"`
+		Detail     int    `json:"detail"`
+		DetailInfo *struct {
+			Tag          string `json:"tag"`
+			NaviLocation struct {
+				Lng float64 `json:"lng"`
+				Lat float64 `json:"lat"`
+			} `json:"navi_location"`
+			NewCatalog    string `json:"new_catalog"`
+			ShopHours     string `json:"shop_hours"`
+			DetailURL     string `json:"detail_url"`
+			Type          string `json:"type"`
+			OverallRating string `json:"overall_rating"`
+			ImageNum      string `json:"image_num"`
+			CommentNum    string `json:"comment_num"`
+			ContentTag    string `json:"content_tag"`
+		} `json:"detail_info,omitempty"`
+	} `json:"result"`
+}
+
+// DistanceMatrixResponse 距离矩阵响应
+type DistanceMatrixResponse struct {
+	Response
+	Result []struct {
+		Distance struct {
+			Text  string `json:"text"`
+			Value string `json:"value"`
+		} `json:"distance"`
+		Duration struct {
+			Text  string `json:"text"`
+			Value string `json:"value"`
+		} `json:"duration"`
+	} `json:"result"`
+}
+
+// DirectionsResponse 路线规划响应
+type DirectionsResponse struct {
+	Response
+	Result struct {
+		Routes []struct {
+			Distance int `json:"distance"`
+			Duration int `json:"duration"`
+			Steps    []struct {
+				Instruction string `json:"instruction"`
+			} `json:"steps"`
+		} `json:"routes"`
+	} `json:"result"`
+}
+
+// WeatherResponse 天气响应
+type WeatherResponse struct {
+	Response
+	Result struct {
+		Location struct {
+			Province string `json:"province"`
+			City     string `json:"city"`
+			Name     string `json:"name"`
+		} `json:"location"`
+		Now struct {
+			Text      string `json:"text"`
+			Temp      int    `json:"temp"`
+			FeelsLike int    `json:"feels_like"`
+			RH        int    `json:"rh"`
+			WindClass string `json:"wind_class"`
+			WindDir   string `json:"wind_dir"`
+			Uptime    int64  `json:"uptime"`
+		} `json:"now"`
+		Forecasts []struct {
+			TextDay   string `json:"text_day"`
+			TextNight string `json:"text_night"`
+			High      int    `json:"high"`
+			Low       int    `json:"low"`
+			WCDay     string `json:"wc_day"`
+			WDDay     string `json:"wd_day"`
+			WCNight   string `json:"wc_night"`
+			WDNight   string `json:"wd_night"`
+			Date      string `json:"date"`
+			Week      string `json:"week"`
+		} `json:"forecasts"`
+		Indexes []struct {
+			Name   string `json:"name"`
+			Brief  string `json:"brief"`
+			Detail string `json:"detail"`
+		} `json:"indexes,omitempty"`
+		Alerts []struct {
+			Type  string `json:"type"`
+			Level string `json:"level"`
+			Title string `json:"title"`
+			Desc  string `json:"desc"`
+		} `json:"alerts"`
+		ForecastHours []struct {
+			Text      string  `json:"text"`
+			TempFC    int     `json:"temp_fc"`
+			WindClass string  `json:"wind_class"`
+			RH        int     `json:"rh"`
+			Prec1H    float64 `json:"prec_1h"`
+			Clouds    int     `json:"clouds"`
+			DataTime  int64   `json:"data_time"`
+		} `json:"forecast_hours,omitempty"`
+	} `json:"result"`
+}
+
+// IPLocationResponse IP定位响应
+type IPLocationResponse struct {
+	Response
+	Address string `json:"address"`
+	Content struct {
+		Address       string `json:"address"`
+		AddressDetail struct {
+			City     string `json:"city"`
+			CityCode int    `json:"city_code"`
+			Province string `json:"province"`
+		} `json:"address_detail"`
+		Point struct {
+			X string `json:"x"`
+			Y string `json:"y"`
+		} `json:"point"`
+	} `json:"content"`
+}
+
+// RoadTrafficResponse 路况响应
+type RoadTrafficResponse struct {
+	Response
+	Description string `json:"description"`
+	Evaluation  struct {
+		Status     int    `json:"status"`
+		StatusDesc string `json:"status_desc"`
+	} `json:"evaluation"`
+	RoadTraffic struct {
+		RoadName           string `json:"road_name"`
+		CongestionSections []struct {
+			SectionDesc        string  `json:"section_desc"`
+			Status             int     `json:"status"`
+			Speed              float64 `json:"speed"`
+			CongestionDistance int     `json:"congestion_distance"`
+			CongestionTrend    string  `json:"congestion_trend"`
+		} `json:"congestion_sections,omitempty"`
+	} `json:"road_traffic"`
+}
+
+// MarkSubmitResponse POI标注提交响应
+type MarkSubmitResponse struct {
+	Response
+	Result struct {
+		SessionID string `json:"session_id"`
+		MapID     string `json:"map_id"`
+	} `json:"result"`
+}
+
+// MarkResultResponse POI标注结果响应
+type MarkResultResponse struct {
+	Response
+	Result struct {
+		Data []struct {
+			AnswerType string `json:"answer_type"`
+			CreateTime string `json:"create_time"`
+			Link       struct {
+				Title   string `json:"title"`
+				Desc    string `json:"desc"`
+				JumpURL string `json:"jump_url"`
+				Image   string `json:"image"`
+				POI     []struct {
+					UID       string  `json:"uid"`
+					Name      string  `json:"name"`
+					Location  any     `json:"location"`
+					AdminInfo any     `json:"admin_info"`
+					Price     float64 `json:"price"`
+					ShopHours string  `json:"shop_hours"`
+				} `json:"poi"`
+			} `json:"link"`
+		} `json:"data"`
+	} `json:"result"`
+}

+ 1182 - 0
mcp-servers/baidu-map/server.go

@@ -0,0 +1,1182 @@
+package baidumap
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"io"
+	"net/http"
+	"net/url"
+	"os"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/bytedance/sonic"
+	mcpservers "github.com/labring/aiproxy/mcp-servers"
+	"github.com/mark3labs/mcp-go/mcp"
+	"github.com/mark3labs/mcp-go/server"
+)
+
+// Configuration templates for the Baidu Map server
+var configTemplates = mcpservers.ConfigTemplates{
+	"baidu_map_api_key": {
+		Name:        "Baidu Map API Key",
+		Required:    mcpservers.ConfigRequiredTypeInitOrReusingOnly,
+		Example:     "your_baidu_map_api_key_here",
+		Description: "Baidu Map API key for accessing map services",
+	},
+	"timeout": {
+		Name:        "Request Timeout",
+		Required:    mcpservers.ConfigRequiredTypeInitOptional,
+		Example:     "30",
+		Description: "Request timeout in seconds (default: 30)",
+		Validator: func(value string) error {
+			timeout, err := strconv.Atoi(value)
+			if err != nil {
+				return errors.New("timeout must be a number")
+			}
+			if timeout < 1 || timeout > 120 {
+				return errors.New("timeout must be between 1 and 120 seconds")
+			}
+			return nil
+		},
+	},
+}
+
+// Server represents the MCP server for Baidu Map
+type Server struct {
+	*server.MCPServer
+	apiKey     string
+	httpClient *http.Client
+}
+
+// NewServer creates a new MCP server for Baidu Map
+func NewServer(config, _ map[string]string) (mcpservers.Server, error) {
+	// Get API key from config or environment
+	apiKey := config["baidu_map_api_key"]
+	if apiKey == "" {
+		apiKey = os.Getenv("BAIDU_MAP_API_KEY")
+	}
+	if apiKey == "" {
+		return nil, errors.New("BAIDU_MAP_API_KEY is required")
+	}
+
+	// Set up timeout
+	timeout := 30 * time.Second
+	if timeoutStr := config["timeout"]; timeoutStr != "" {
+		if t, err := strconv.Atoi(timeoutStr); err == nil {
+			timeout = time.Duration(t) * time.Second
+		}
+	}
+
+	// Create HTTP client
+	httpClient := &http.Client{
+		Timeout: timeout,
+	}
+
+	// Create MCP server
+	mcpServer := server.NewMCPServer(
+		"mcp-server/baidu-map",
+		"1.0.0",
+	)
+
+	baiduServer := &Server{
+		MCPServer:  mcpServer,
+		apiKey:     apiKey,
+		httpClient: httpClient,
+	}
+
+	// Add all tools
+	baiduServer.addTools()
+
+	return baiduServer, nil
+}
+
+// addTools adds all Baidu Map tools to the server
+func (s *Server) addTools() {
+	s.addGeocodeTools()
+	s.addSearchTools()
+	s.addRoutingTools()
+	s.addWeatherTool()
+	s.addIPLocationTool()
+	s.addTrafficTool()
+	s.addPOIExtractTool()
+}
+
+// addGeocodeTools adds geocoding and reverse geocoding tools
+func (s *Server) addGeocodeTools() {
+	// Geocode tool
+	s.AddTool(
+		mcp.Tool{
+			Name:        "map_geocode",
+			Description: "地理编码服务",
+			InputSchema: mcp.ToolInputSchema{
+				Type: "object",
+				Properties: map[string]any{
+					"address": map[string]any{
+						"type":        "string",
+						"description": "待解析的地址(最多支持84个字节。可以输入两种样式的值,分别是:1、标准的结构化地址信息,如北京市海淀区上地十街十号【推荐,地址结构越完整,解析精度越高】2、支持\"*路与*路交叉口\"描述方式,如北一环路和阜阳路的交叉路口第二种方式并不总是有返回结果,只有当地址库中存在该地址描述时才有返回。)",
+					},
+				},
+				Required: []string{"address"},
+			},
+		},
+		s.handleGeocode,
+	)
+
+	// Reverse geocode tool
+	s.AddTool(
+		mcp.Tool{
+			Name:        "map_reverse_geocode",
+			Description: "全球逆地理编码",
+			InputSchema: mcp.ToolInputSchema{
+				Type: "object",
+				Properties: map[string]any{
+					"latitude": map[string]any{
+						"type":        "number",
+						"description": "Latitude coordinate",
+					},
+					"longitude": map[string]any{
+						"type":        "number",
+						"description": "Longitude coordinate",
+					},
+				},
+				Required: []string{"latitude", "longitude"},
+			},
+		},
+		s.handleReverseGeocode,
+	)
+}
+
+// addSearchTools adds place search and details tools
+func (s *Server) addSearchTools() {
+	// Place search tool
+	s.AddTool(
+		mcp.Tool{
+			Name:        "map_search_places",
+			Description: "地点检索服务(包括城市检索、圆形区域检索、多边形区域检索)",
+			InputSchema: mcp.ToolInputSchema{
+				Type: "object",
+				Properties: map[string]any{
+					"query": map[string]any{
+						"type":        "string",
+						"description": "检索关键字",
+					},
+					"region": map[string]any{
+						"type":        "string",
+						"description": "检索行政区划区域",
+					},
+					"bounds": map[string]any{
+						"type":        "string",
+						"description": "检索多边形区域",
+					},
+					"location": map[string]any{
+						"type":        "string",
+						"description": "圆形区域检索中心点,不支持多个点",
+					},
+				},
+				Required: []string{"query"},
+			},
+		},
+		s.handlePlaceSearch,
+	)
+
+	// Place details tool
+	s.AddTool(
+		mcp.Tool{
+			Name:        "map_place_details",
+			Description: "地点详情检索服务",
+			InputSchema: mcp.ToolInputSchema{
+				Type: "object",
+				Properties: map[string]any{
+					"uid": map[string]any{
+						"type":        "string",
+						"description": "poi的uid",
+					},
+					"scope": map[string]any{
+						"type":        "string",
+						"description": "检索结果详细程度。取值为1 或空,则返回基本信息;取值为2,返回检索POI详细信息",
+					},
+				},
+				Required: []string{"uid"},
+			},
+		},
+		s.handlePlaceDetails,
+	)
+}
+
+// addRoutingTools adds distance matrix and directions tools
+func (s *Server) addRoutingTools() {
+	// Distance matrix tool
+	s.AddTool(
+		mcp.Tool{
+			Name:        "map_distance_matrix",
+			Description: "计算多个出发地和目的地的距离和路线用时",
+			InputSchema: mcp.ToolInputSchema{
+				Type: "object",
+				Properties: map[string]any{
+					"origins": map[string]any{
+						"type":        "array",
+						"items":       map[string]any{"type": "string"},
+						"description": "起点的纬度,经度。",
+					},
+					"destinations": map[string]any{
+						"type":        "array",
+						"items":       map[string]any{"type": "string"},
+						"description": "终点的纬度,经度。",
+					},
+					"mode": map[string]any{
+						"type":        "string",
+						"description": "路线类型,可选值:driving(驾车)、walking(步行)、riding(骑行)、motorcycle(摩托车)",
+						"enum":        []string{"driving", "walking", "riding", "motorcycle"},
+						"default":     "driving",
+					},
+				},
+				Required: []string{"origins", "destinations"},
+			},
+		},
+		s.handleDistanceMatrix,
+	)
+
+	// Directions tool
+	s.AddTool(
+		mcp.Tool{
+			Name:        "map_directions",
+			Description: "路线规划服务, 计算出发地到目的地的距离、路线用时、路线方案",
+			InputSchema: mcp.ToolInputSchema{
+				Type: "object",
+				Properties: map[string]any{
+					"origin": map[string]any{
+						"type":        "string",
+						"description": "起点经纬度,格式为:纬度,经度;小数点后不超过6位,40.056878,116.30815",
+					},
+					"destination": map[string]any{
+						"type":        "string",
+						"description": "终点经纬度,格式为:纬度,经度;小数点后不超过6位,40.056878,116.30815",
+					},
+					"mode": map[string]any{
+						"type":        "string",
+						"description": "路线规划类型,可选值:driving(驾车)、walking(步行)、riding(骑行)、transit(公交)",
+						"enum":        []string{"driving", "walking", "riding", "transit"},
+						"default":     "driving",
+					},
+				},
+				Required: []string{"origin", "destination"},
+			},
+		},
+		s.handleDirections,
+	)
+}
+
+// addWeatherTool adds weather tool
+func (s *Server) addWeatherTool() {
+	s.AddTool(
+		mcp.Tool{
+			Name:        "map_weather",
+			Description: "通过行政区划代码或者经纬度坐标获取实时天气信息和未来5天天气预报",
+			InputSchema: mcp.ToolInputSchema{
+				Type: "object",
+				Properties: map[string]any{
+					"districtId": map[string]any{
+						"type":        "string",
+						"description": "行政区划代码(适用于区、县级别)",
+					},
+					"location": map[string]any{
+						"type":        "string",
+						"description": "经纬度,经度在前纬度在后,逗号分隔,格式如116.404,39.915",
+					},
+				},
+			},
+		},
+		s.handleWeather,
+	)
+}
+
+// addIPLocationTool adds IP location tool
+func (s *Server) addIPLocationTool() {
+	s.AddTool(
+		mcp.Tool{
+			Name:        "map_ip_location",
+			Description: "通过IP地址获取位置信息",
+			InputSchema: mcp.ToolInputSchema{
+				Type: "object",
+				Properties: map[string]any{
+					"ip": map[string]any{
+						"type":        "string",
+						"description": "IP地址",
+					},
+				},
+				Required: []string{"ip"},
+			},
+		},
+		s.handleIPLocation,
+	)
+}
+
+// addTrafficTool adds road traffic tool
+func (s *Server) addTrafficTool() {
+	s.AddTool(
+		mcp.Tool{
+			Name:        "map_road_traffic",
+			Description: "根据城市和道路名称查询具体道路的实时拥堵评价和拥堵路段、拥堵距离、拥堵趋势等信息",
+			InputSchema: mcp.ToolInputSchema{
+				Type: "object",
+				Properties: map[string]any{
+					"roadName": map[string]any{
+						"type":        "string",
+						"description": "道路名称",
+					},
+					"city": map[string]any{
+						"type":        "string",
+						"description": "城市名称",
+					},
+					"bounds": map[string]any{
+						"type":        "string",
+						"description": "矩形区域,左下角和右上角的经纬度坐标点,坐标对间使用;号分隔,格式为:纬度,经度;纬度,经度,如39.912078,116.464303;39.918276,116.475442",
+					},
+					"vertexes": map[string]any{
+						"type":        "string",
+						"description": "多边形边界点,经纬度顺序为:纬度,经度; 顶点顺序需按逆时针排列, 格式如vertexes=39.910528,116.472926;39.918276,116.475442;39.916671,116.459056;39.912078,116.464303",
+					},
+					"center": map[string]any{
+						"type":        "string",
+						"description": "中心点坐标,如39.912078,116.464303",
+					},
+					"radius": map[string]any{
+						"type":        "number",
+						"description": "查询半径,单位:米",
+					},
+				},
+			},
+		},
+		s.handleRoadTraffic,
+	)
+}
+
+// addPOIExtractTool adds POI extract tool
+func (s *Server) addPOIExtractTool() {
+	s.AddTool(
+		mcp.Tool{
+			Name:        "map_poi_extract",
+			Description: "POI智能标注",
+			InputSchema: mcp.ToolInputSchema{
+				Type: "object",
+				Properties: map[string]any{
+					"textContent": map[string]any{
+						"type":        "string",
+						"description": "描述POI的文本内容",
+					},
+				},
+				Required: []string{"textContent"},
+			},
+		},
+		s.handlePOIExtract,
+	)
+}
+
+// Tool handlers
+
+// handleGeocode handles geocoding requests
+func (s *Server) handleGeocode(
+	ctx context.Context,
+	request mcp.CallToolRequest,
+) (*mcp.CallToolResult, error) {
+	args := request.GetArguments()
+
+	address, ok := args["address"].(string)
+	if !ok || address == "" {
+		return nil, errors.New("address is required")
+	}
+
+	apiURL := "https://api.map.baidu.com/geocoding/v3/"
+	params := url.Values{}
+	params.Add("address", address)
+	params.Add("ak", s.apiKey)
+	params.Add("output", "json")
+	params.Add("from", "node_mcp")
+
+	fullURL := apiURL + "?" + params.Encode()
+
+	req, err := http.NewRequestWithContext(ctx, http.MethodGet, fullURL, nil)
+	if err != nil {
+		return nil, fmt.Errorf("failed to create request: %w", err)
+	}
+
+	resp, err := s.httpClient.Do(req)
+	if err != nil {
+		return nil, fmt.Errorf("request failed: %w", err)
+	}
+	defer resp.Body.Close()
+
+	body, err := io.ReadAll(resp.Body)
+	if err != nil {
+		return nil, fmt.Errorf("failed to read response: %w", err)
+	}
+
+	var geocodeResp GeocodeResponse
+	if err := sonic.Unmarshal(body, &geocodeResp); err != nil {
+		return nil, fmt.Errorf("failed to parse response: %w", err)
+	}
+
+	if geocodeResp.Status != 0 {
+		return mcp.NewToolResultError(
+			"Geocoding failed: " + getErrorMessage(geocodeResp.Response),
+		), nil
+	}
+
+	result := map[string]any{
+		"location":      geocodeResp.Result.Location,
+		"precise":       geocodeResp.Result.Precise,
+		"confidence":    geocodeResp.Result.Confidence,
+		"comprehension": geocodeResp.Result.Comprehension,
+		"level":         geocodeResp.Result.Level,
+	}
+
+	resultJSON, _ := sonic.MarshalIndent(result, "", "  ")
+	return mcp.NewToolResultText(string(resultJSON)), nil
+}
+
+// handleReverseGeocode handles reverse geocoding requests
+func (s *Server) handleReverseGeocode(
+	ctx context.Context,
+	request mcp.CallToolRequest,
+) (*mcp.CallToolResult, error) {
+	args := request.GetArguments()
+
+	latitude, ok := args["latitude"].(float64)
+	if !ok {
+		return nil, errors.New("latitude is required")
+	}
+
+	longitude, ok := args["longitude"].(float64)
+	if !ok {
+		return nil, errors.New("longitude is required")
+	}
+
+	apiURL := "https://api.map.baidu.com/reverse_geocoding/v3/"
+	params := url.Values{}
+	params.Add("location", fmt.Sprintf("%f,%f", latitude, longitude))
+	params.Add("extensions_poi", "1")
+	params.Add("ak", s.apiKey)
+	params.Add("output", "json")
+	params.Add("from", "node_mcp")
+
+	fullURL := apiURL + "?" + params.Encode()
+
+	req, err := http.NewRequestWithContext(ctx, http.MethodGet, fullURL, nil)
+	if err != nil {
+		return nil, fmt.Errorf("failed to create request: %w", err)
+	}
+
+	resp, err := s.httpClient.Do(req)
+	if err != nil {
+		return nil, fmt.Errorf("request failed: %w", err)
+	}
+	defer resp.Body.Close()
+
+	body, err := io.ReadAll(resp.Body)
+	if err != nil {
+		return nil, fmt.Errorf("failed to read response: %w", err)
+	}
+
+	var reverseResp ReverseGeocodeResponse
+	if err := sonic.Unmarshal(body, &reverseResp); err != nil {
+		return nil, fmt.Errorf("failed to parse response: %w", err)
+	}
+
+	if reverseResp.Status != 0 {
+		return mcp.NewToolResultError(
+			"Reverse geocoding failed: " + getErrorMessage(reverseResp.Response),
+		), nil
+	}
+
+	var placeID *string
+	if len(reverseResp.Result.POIs) > 0 {
+		// Note: POIs is []interface{}, need to handle properly if needed
+		placeID = nil
+	}
+
+	result := map[string]any{
+		"place_id":              placeID,
+		"location":              reverseResp.Result.Location,
+		"formatted_address":     reverseResp.Result.FormattedAddress,
+		"formatted_address_poi": reverseResp.Result.FormattedAddressPOI,
+		"business":              reverseResp.Result.Business,
+		"business_info":         reverseResp.Result.BusinessInfo,
+		"addressComponent":      reverseResp.Result.AddressComponent,
+		"edz":                   reverseResp.Result.EDZ,
+		"pois":                  reverseResp.Result.POIs,
+		"roads":                 reverseResp.Result.Roads,
+		"poiRegions":            reverseResp.Result.POIRegions,
+		"sematic_description":   reverseResp.Result.SematicDescription,
+		"cityCode":              reverseResp.Result.CityCode,
+	}
+
+	resultJSON, _ := sonic.MarshalIndent(result, "", "  ")
+	return mcp.NewToolResultText(string(resultJSON)), nil
+}
+
+// handlePlaceSearch handles place search requests
+func (s *Server) handlePlaceSearch(
+	ctx context.Context,
+	request mcp.CallToolRequest,
+) (*mcp.CallToolResult, error) {
+	args := request.GetArguments()
+
+	query, ok := args["query"].(string)
+	if !ok || query == "" {
+		return nil, errors.New("query is required")
+	}
+
+	apiURL := "https://api.map.baidu.com/place/v2/search"
+	params := url.Values{}
+	params.Add("query", query)
+	params.Add("ak", s.apiKey)
+	params.Add("output", "json")
+	params.Add("from", "node_mcp")
+
+	if region, ok := args["region"].(string); ok && region != "" {
+		params.Add("region", region)
+	}
+	if bounds, ok := args["bounds"].(string); ok && bounds != "" {
+		params.Add("bounds", bounds)
+	}
+	if location, ok := args["location"].(string); ok && location != "" {
+		params.Add("location", location)
+	}
+
+	fullURL := apiURL + "?" + params.Encode()
+
+	req, err := http.NewRequestWithContext(ctx, http.MethodGet, fullURL, nil)
+	if err != nil {
+		return nil, fmt.Errorf("failed to create request: %w", err)
+	}
+
+	resp, err := s.httpClient.Do(req)
+	if err != nil {
+		return nil, fmt.Errorf("request failed: %w", err)
+	}
+	defer resp.Body.Close()
+
+	body, err := io.ReadAll(resp.Body)
+	if err != nil {
+		return nil, fmt.Errorf("failed to read response: %w", err)
+	}
+
+	var searchResp PlacesSearchResponse
+	if err := sonic.Unmarshal(body, &searchResp); err != nil {
+		return nil, fmt.Errorf("failed to parse response: %w", err)
+	}
+
+	if searchResp.Status != 0 {
+		return mcp.NewToolResultError(
+			"Place search failed: " + getErrorMessage(searchResp.Response),
+		), nil
+	}
+
+	// Handle different response structures
+	places := searchResp.Results
+	if len(places) == 0 && len(searchResp.Result) > 0 {
+		places = searchResp.Result
+	}
+
+	results := make([]map[string]any, 0, len(places))
+	for _, place := range places {
+		results = append(results, map[string]any{
+			"name":      place.Name,
+			"location":  place.Location,
+			"address":   place.Address,
+			"province":  place.Province,
+			"city":      place.City,
+			"area":      place.Area,
+			"street_id": place.StreetID,
+			"telephone": place.Telephone,
+			"detail":    place.Detail,
+			"uid":       place.UID,
+		})
+	}
+
+	result := map[string]any{
+		"result_type": searchResp.ResultType,
+		"query_type":  searchResp.QueryType,
+		"results":     results,
+	}
+
+	resultJSON, _ := sonic.MarshalIndent(result, "", "  ")
+	return mcp.NewToolResultText(string(resultJSON)), nil
+}
+
+// handlePlaceDetails handles place details requests
+func (s *Server) handlePlaceDetails(
+	ctx context.Context,
+	request mcp.CallToolRequest,
+) (*mcp.CallToolResult, error) {
+	args := request.GetArguments()
+
+	uid, ok := args["uid"].(string)
+	if !ok || uid == "" {
+		return nil, errors.New("uid is required")
+	}
+
+	apiURL := "https://api.map.baidu.com/place/v2/detail"
+	params := url.Values{}
+	params.Add("uid", uid)
+	params.Add("ak", s.apiKey)
+	params.Add("output", "json")
+	params.Add("from", "node_mcp")
+
+	if scope, ok := args["scope"].(string); ok && scope != "" {
+		params.Add("scope", scope)
+	}
+
+	fullURL := apiURL + "?" + params.Encode()
+
+	req, err := http.NewRequestWithContext(ctx, http.MethodGet, fullURL, nil)
+	if err != nil {
+		return nil, fmt.Errorf("failed to create request: %w", err)
+	}
+
+	resp, err := s.httpClient.Do(req)
+	if err != nil {
+		return nil, fmt.Errorf("request failed: %w", err)
+	}
+	defer resp.Body.Close()
+
+	body, err := io.ReadAll(resp.Body)
+	if err != nil {
+		return nil, fmt.Errorf("failed to read response: %w", err)
+	}
+
+	var detailResp PlaceDetailsResponse
+	if err := sonic.Unmarshal(body, &detailResp); err != nil {
+		return nil, fmt.Errorf("failed to parse response: %w", err)
+	}
+
+	if detailResp.Status != 0 {
+		return mcp.NewToolResultError(
+			"Place details request failed: " + getErrorMessage(detailResp.Response),
+		), nil
+	}
+
+	result := map[string]any{
+		"uid":       detailResp.Result.UID,
+		"name":      detailResp.Result.Name,
+		"location":  detailResp.Result.Location,
+		"address":   detailResp.Result.Address,
+		"province":  detailResp.Result.Province,
+		"city":      detailResp.Result.City,
+		"area":      detailResp.Result.Area,
+		"street_id": detailResp.Result.StreetID,
+		"detail":    detailResp.Result.Detail,
+	}
+
+	if detailResp.Result.DetailInfo != nil {
+		result["detail_info"] = detailResp.Result.DetailInfo
+	}
+
+	resultJSON, _ := sonic.MarshalIndent(result, "", "  ")
+	return mcp.NewToolResultText(string(resultJSON)), nil
+}
+
+// handleDistanceMatrix handles distance matrix requests
+func (s *Server) handleDistanceMatrix(
+	ctx context.Context,
+	request mcp.CallToolRequest,
+) (*mcp.CallToolResult, error) {
+	args := request.GetArguments()
+
+	originsRaw, ok := args["origins"].([]any)
+	if !ok {
+		return nil, errors.New("origins is required")
+	}
+
+	destinationsRaw, ok := args["destinations"].([]any)
+	if !ok {
+		return nil, errors.New("destinations is required")
+	}
+
+	mode := "driving"
+	if m, ok := args["mode"].(string); ok && m != "" {
+		mode = m
+	}
+
+	// Convert origins and destinations to strings
+	var origins, destinations []string
+	for _, o := range originsRaw {
+		if s, ok := o.(string); ok {
+			origins = append(origins, s)
+		}
+	}
+	for _, d := range destinationsRaw {
+		if s, ok := d.(string); ok {
+			destinations = append(destinations, s)
+		}
+	}
+
+	apiURL := "https://api.map.baidu.com/routematrix/v2/" + mode
+	params := url.Values{}
+	params.Add("origins", strings.Join(origins, "|"))
+	params.Add("destinations", strings.Join(destinations, "|"))
+	params.Add("ak", s.apiKey)
+	params.Add("output", "json")
+	params.Add("from", "node_mcp")
+
+	fullURL := apiURL + "?" + params.Encode()
+
+	req, err := http.NewRequestWithContext(ctx, http.MethodGet, fullURL, nil)
+	if err != nil {
+		return nil, fmt.Errorf("failed to create request: %w", err)
+	}
+
+	resp, err := s.httpClient.Do(req)
+	if err != nil {
+		return nil, fmt.Errorf("request failed: %w", err)
+	}
+	defer resp.Body.Close()
+
+	body, err := io.ReadAll(resp.Body)
+	if err != nil {
+		return nil, fmt.Errorf("failed to read response: %w", err)
+	}
+
+	var matrixResp DistanceMatrixResponse
+	if err := sonic.Unmarshal(body, &matrixResp); err != nil {
+		return nil, fmt.Errorf("failed to parse response: %w", err)
+	}
+
+	if matrixResp.Status != 0 {
+		return mcp.NewToolResultError(
+			"Distance matrix request failed: " + getErrorMessage(matrixResp.Response),
+		), nil
+	}
+
+	results := make([]map[string]any, 0, len(matrixResp.Result))
+	for _, row := range matrixResp.Result {
+		results = append(results, map[string]any{
+			"elements": map[string]any{
+				"duration": row.Duration,
+				"distance": row.Distance,
+			},
+		})
+	}
+
+	result := map[string]any{
+		"results": results,
+	}
+
+	resultJSON, _ := sonic.MarshalIndent(result, "", "  ")
+	return mcp.NewToolResultText(string(resultJSON)), nil
+}
+
+// handleDirections handles directions requests
+func (s *Server) handleDirections(
+	ctx context.Context,
+	request mcp.CallToolRequest,
+) (*mcp.CallToolResult, error) {
+	args := request.GetArguments()
+
+	origin, ok := args["origin"].(string)
+	if !ok || origin == "" {
+		return nil, errors.New("origin is required")
+	}
+
+	destination, ok := args["destination"].(string)
+	if !ok || destination == "" {
+		return nil, errors.New("destination is required")
+	}
+
+	mode := "driving"
+	if m, ok := args["mode"].(string); ok && m != "" {
+		mode = m
+	}
+
+	apiURL := "https://api.map.baidu.com/directionlite/v1/" + mode
+	params := url.Values{}
+	params.Add("origin", origin)
+	params.Add("destination", destination)
+	params.Add("ak", s.apiKey)
+	params.Add("from", "node_mcp")
+
+	fullURL := apiURL + "?" + params.Encode()
+
+	req, err := http.NewRequestWithContext(ctx, http.MethodGet, fullURL, nil)
+	if err != nil {
+		return nil, fmt.Errorf("failed to create request: %w", err)
+	}
+
+	resp, err := s.httpClient.Do(req)
+	if err != nil {
+		return nil, fmt.Errorf("request failed: %w", err)
+	}
+	defer resp.Body.Close()
+
+	body, err := io.ReadAll(resp.Body)
+	if err != nil {
+		return nil, fmt.Errorf("failed to read response: %w", err)
+	}
+
+	var directionsResp DirectionsResponse
+	if err := sonic.Unmarshal(body, &directionsResp); err != nil {
+		return nil, fmt.Errorf("failed to parse response: %w", err)
+	}
+
+	if directionsResp.Status != 0 {
+		return mcp.NewToolResultError(
+			"Directions request failed: " + getErrorMessage(directionsResp.Response),
+		), nil
+	}
+
+	routes := make([]map[string]any, 0, len(directionsResp.Result.Routes))
+	for _, route := range directionsResp.Result.Routes {
+		steps := make([]map[string]any, 0, len(route.Steps))
+		for _, step := range route.Steps {
+			steps = append(steps, map[string]any{
+				"instructions": step.Instruction,
+			})
+		}
+
+		routes = append(routes, map[string]any{
+			"distance": route.Distance,
+			"duration": route.Duration,
+			"steps":    steps,
+		})
+	}
+
+	result := map[string]any{
+		"routes": routes,
+	}
+
+	resultJSON, _ := sonic.MarshalIndent(result, "", "  ")
+	return mcp.NewToolResultText(string(resultJSON)), nil
+}
+
+// handleWeather handles weather requests
+func (s *Server) handleWeather(
+	ctx context.Context,
+	request mcp.CallToolRequest,
+) (*mcp.CallToolResult, error) {
+	args := request.GetArguments()
+
+	apiURL := "https://api.map.baidu.com/weather/v1/"
+	params := url.Values{}
+	params.Add("data_type", "all")
+	params.Add("coordtype", "bd09ll")
+	params.Add("ak", s.apiKey)
+	params.Add("from", "node_mcp")
+
+	if location, ok := args["location"].(string); ok && location != "" {
+		params.Add("location", location)
+	}
+	if districtID, ok := args["districtId"].(string); ok && districtID != "" {
+		params.Add("district_id", districtID)
+	}
+
+	fullURL := apiURL + "?" + params.Encode()
+
+	req, err := http.NewRequestWithContext(ctx, http.MethodGet, fullURL, nil)
+	if err != nil {
+		return nil, fmt.Errorf("failed to create request: %w", err)
+	}
+
+	resp, err := s.httpClient.Do(req)
+	if err != nil {
+		return nil, fmt.Errorf("request failed: %w", err)
+	}
+	defer resp.Body.Close()
+
+	body, err := io.ReadAll(resp.Body)
+	if err != nil {
+		return nil, fmt.Errorf("failed to read response: %w", err)
+	}
+
+	var weatherResp WeatherResponse
+	if err := sonic.Unmarshal(body, &weatherResp); err != nil {
+		return nil, fmt.Errorf("failed to parse response: %w", err)
+	}
+
+	if weatherResp.Status != 0 {
+		return mcp.NewToolResultError(
+			"Weather search failed: " + getErrorMessage(weatherResp.Response),
+		), nil
+	}
+
+	result := map[string]any{
+		"location":       weatherResp.Result.Location,
+		"now":            weatherResp.Result.Now,
+		"forecasts":      weatherResp.Result.Forecasts,
+		"forecast_hours": weatherResp.Result.ForecastHours,
+		"indexes":        weatherResp.Result.Indexes,
+		"alerts":         weatherResp.Result.Alerts,
+	}
+
+	resultJSON, _ := sonic.MarshalIndent(result, "", "  ")
+	return mcp.NewToolResultText(string(resultJSON)), nil
+}
+
+// handleIPLocation handles IP location requests
+func (s *Server) handleIPLocation(
+	ctx context.Context,
+	request mcp.CallToolRequest,
+) (*mcp.CallToolResult, error) {
+	args := request.GetArguments()
+
+	ip, ok := args["ip"].(string)
+	if !ok || ip == "" {
+		return nil, errors.New("ip is required")
+	}
+
+	apiURL := "https://api.map.baidu.com/location/ip"
+	params := url.Values{}
+	params.Add("ip", ip)
+	params.Add("coor", "bd09ll")
+	params.Add("ak", s.apiKey)
+	params.Add("from", "node_mcp")
+
+	fullURL := apiURL + "?" + params.Encode()
+
+	req, err := http.NewRequestWithContext(ctx, http.MethodGet, fullURL, nil)
+	if err != nil {
+		return nil, fmt.Errorf("failed to create request: %w", err)
+	}
+
+	resp, err := s.httpClient.Do(req)
+	if err != nil {
+		return nil, fmt.Errorf("request failed: %w", err)
+	}
+	defer resp.Body.Close()
+
+	body, err := io.ReadAll(resp.Body)
+	if err != nil {
+		return nil, fmt.Errorf("failed to read response: %w", err)
+	}
+
+	var ipResp IPLocationResponse
+	if err := sonic.Unmarshal(body, &ipResp); err != nil {
+		return nil, fmt.Errorf("failed to parse response: %w", err)
+	}
+
+	if ipResp.Status != 0 {
+		return mcp.NewToolResultError(
+			"IP address search failed: " + getErrorMessage(ipResp.Response),
+		), nil
+	}
+
+	result := map[string]any{
+		"formatted_address": ipResp.Address,
+		"address_detail":    ipResp.Content.AddressDetail,
+		"point":             ipResp.Content.Point,
+	}
+
+	resultJSON, _ := sonic.MarshalIndent(result, "", "  ")
+	return mcp.NewToolResultText(string(resultJSON)), nil
+}
+
+// handleRoadTraffic handles road traffic requests
+func (s *Server) handleRoadTraffic(
+	ctx context.Context,
+	request mcp.CallToolRequest,
+) (*mcp.CallToolResult, error) {
+	args := request.GetArguments()
+
+	baseURL := "https://api.map.baidu.com"
+	params := url.Values{}
+	params.Add("ak", s.apiKey)
+	params.Add("from", "node_mcp")
+
+	var apiURL string
+
+	// Determine API endpoint based on parameters
+	if roadName, ok := args["roadName"].(string); ok && roadName != "" {
+		if city, ok := args["city"].(string); ok && city != "" {
+			apiURL = baseURL + "/traffic/v1/road"
+			params.Add("road_name", roadName)
+			params.Add("city", city)
+		}
+	} else if bounds, ok := args["bounds"].(string); ok && bounds != "" {
+		apiURL = baseURL + "/traffic/v1/bound"
+		params.Add("bounds", bounds)
+	} else if vertexes, ok := args["vertexes"].(string); ok && vertexes != "" {
+		apiURL = baseURL + "/traffic/v1/polygon"
+		params.Add("vertexes", vertexes)
+	} else if center, ok := args["center"].(string); ok && center != "" {
+		if radius, ok := args["radius"].(float64); ok {
+			apiURL = baseURL + "/traffic/v1/around"
+			params.Add("center", center)
+			params.Add("radius", strconv.FormatFloat(radius, 'f', -1, 64))
+		}
+	}
+
+	if apiURL == "" {
+		return nil, errors.New("insufficient parameters for road traffic query")
+	}
+
+	fullURL := apiURL + "?" + params.Encode()
+
+	req, err := http.NewRequestWithContext(ctx, http.MethodGet, fullURL, nil)
+	if err != nil {
+		return nil, fmt.Errorf("failed to create request: %w", err)
+	}
+
+	resp, err := s.httpClient.Do(req)
+	if err != nil {
+		return nil, fmt.Errorf("request failed: %w", err)
+	}
+	defer resp.Body.Close()
+
+	body, err := io.ReadAll(resp.Body)
+	if err != nil {
+		return nil, fmt.Errorf("failed to read response: %w", err)
+	}
+
+	var trafficResp RoadTrafficResponse
+	if err := sonic.Unmarshal(body, &trafficResp); err != nil {
+		return nil, fmt.Errorf("failed to parse response: %w", err)
+	}
+
+	if trafficResp.Status != 0 {
+		return mcp.NewToolResultError(
+			"Road traffic search failed: " + getErrorMessage(trafficResp.Response),
+		), nil
+	}
+
+	result := map[string]any{
+		"description":  trafficResp.Description,
+		"evaluation":   trafficResp.Evaluation,
+		"road_traffic": trafficResp.RoadTraffic,
+	}
+
+	resultJSON, _ := sonic.MarshalIndent(result, "", "  ")
+	return mcp.NewToolResultText(string(resultJSON)), nil
+}
+
+// handlePOIExtract handles POI extraction requests
+func (s *Server) handlePOIExtract(
+	ctx context.Context,
+	request mcp.CallToolRequest,
+) (*mcp.CallToolResult, error) {
+	args := request.GetArguments()
+
+	textContent, ok := args["textContent"].(string)
+	if !ok || textContent == "" {
+		return nil, errors.New("textContent is required")
+	}
+
+	// Submit POI extraction request
+	submitURL := "https://api.map.baidu.com/api_mark/v1/submit"
+	params := url.Values{}
+	params.Add("text_content", textContent)
+	params.Add("id", "75274677") // Device ID
+	params.Add("msg_type", "text")
+	params.Add("ak", s.apiKey)
+	params.Add("from", "node_mcp")
+
+	req, err := http.NewRequestWithContext(
+		ctx,
+		http.MethodPost,
+		submitURL,
+		strings.NewReader(params.Encode()),
+	)
+	if err != nil {
+		return nil, fmt.Errorf("submit request failed: %w", err)
+	}
+	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+
+	submitResp, err := s.httpClient.Do(req)
+	if err != nil {
+		return nil, fmt.Errorf("submit request failed: %w", err)
+	}
+	defer submitResp.Body.Close()
+
+	submitBody, err := io.ReadAll(submitResp.Body)
+	if err != nil {
+		return nil, fmt.Errorf("failed to read submit response: %w", err)
+	}
+
+	var submitData MarkSubmitResponse
+	if err := sonic.Unmarshal(submitBody, &submitData); err != nil {
+		return nil, fmt.Errorf("failed to parse submit response: %w", err)
+	}
+
+	if submitData.Status != 0 {
+		return mcp.NewToolResultError(
+			"Mark submit failed: " + getErrorMessage(submitData.Response),
+		), nil
+	}
+
+	// Poll for results
+	resultURL := "https://api.map.baidu.com/api_mark/v1/result"
+	mapID := submitData.Result.MapID
+
+	resultParams := url.Values{}
+	resultParams.Add("map_id", mapID)
+	resultParams.Add("ak", s.apiKey)
+	resultParams.Add("from", "node_mcp")
+
+	// Poll every 1 second for up to 20 seconds
+	maxTime := 20 * time.Second
+	intervalTime := 1 * time.Second
+	startTime := time.Now()
+
+	for time.Since(startTime) < maxTime {
+		req, err := http.NewRequestWithContext(
+			ctx,
+			http.MethodPost,
+			resultURL,
+			strings.NewReader(resultParams.Encode()),
+		)
+		if err != nil {
+			return nil, fmt.Errorf("failed to create request: %w", err)
+		}
+		req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+
+		resultResp, err := s.httpClient.Do(req)
+		if err != nil {
+			time.Sleep(intervalTime)
+			continue
+		}
+		defer resultResp.Body.Close()
+
+		resultBody, err := io.ReadAll(resultResp.Body)
+		if err != nil {
+			time.Sleep(intervalTime)
+			continue
+		}
+
+		var resultData MarkResultResponse
+		if err := sonic.Unmarshal(resultBody, &resultData); err != nil {
+			time.Sleep(intervalTime)
+			continue
+		}
+
+		if len(resultData.Result.Data) > 0 {
+			result := map[string]any{
+				"jumpUrl": resultData.Result.Data[0].Link.JumpURL,
+				"title":   resultData.Result.Data[0].Link.Title,
+				"desc":    resultData.Result.Data[0].Link.Desc,
+				"image":   resultData.Result.Data[0].Link.Image,
+				"poi":     resultData.Result.Data[0].Link.POI,
+			}
+
+			resultJSON, _ := sonic.MarshalIndent(result, "", "  ")
+			return mcp.NewToolResultText(string(resultJSON)), nil
+		}
+
+		time.Sleep(intervalTime)
+	}
+
+	return mcp.NewToolResultError(
+		"POI result is null",
+	), nil
+}
+
+// getErrorMessage extracts error message from response
+func getErrorMessage(resp Response) string {
+	if resp.Message != "" {
+		return resp.Message
+	}
+	if resp.Msg != "" {
+		return resp.Msg
+	}
+	return strconv.Itoa(resp.Status)
+}

+ 4 - 5
mcp-servers/jina-tools/jina.go

@@ -3,7 +3,6 @@ package jinatools
 import (
 	"bytes"
 	"context"
-	"encoding/json"
 	"errors"
 	"fmt"
 	"io"
@@ -295,7 +294,7 @@ func (s *JinaServer) handleJinaReader(
 		URL: urlStr,
 	}
 
-	jsonBody, err := json.Marshal(reqBody)
+	jsonBody, err := sonic.Marshal(reqBody)
 	if err != nil {
 		return nil, fmt.Errorf("failed to marshal request: %w", err)
 	}
@@ -337,7 +336,7 @@ func (s *JinaServer) handleJinaReader(
 
 	// Parse response
 	var jinaResp JinaReaderResponse
-	if err := json.Unmarshal(respBody, &jinaResp); err != nil {
+	if err := sonic.Unmarshal(respBody, &jinaResp); err != nil {
 		// If parsing fails, return raw response
 		return mcp.NewToolResultText(string(respBody)), nil
 	}
@@ -414,7 +413,7 @@ func (s *JinaServer) handleJinaSearch(
 
 	// Parse response
 	var searchResp JinaSearchResponse
-	if err := json.Unmarshal(respBody, &searchResp); err != nil {
+	if err := sonic.Unmarshal(respBody, &searchResp); err != nil {
 		return nil, fmt.Errorf("failed to parse response: %w", err)
 	}
 
@@ -511,7 +510,7 @@ func (s *JinaServer) handleJinaFactCheck(
 		Deepdive:  deepdive,
 	}
 
-	jsonBody, err := json.Marshal(reqBody)
+	jsonBody, err := sonic.Marshal(reqBody)
 	if err != nil {
 		return nil, fmt.Errorf("failed to marshal request: %w", err)
 	}

+ 1 - 0
mcp-servers/mcpregister/init.go

@@ -6,6 +6,7 @@ import (
 	_ "github.com/labring/aiproxy/mcp-servers/aiproxy-openapi"
 	_ "github.com/labring/aiproxy/mcp-servers/alipay"
 	_ "github.com/labring/aiproxy/mcp-servers/amap"
+	_ "github.com/labring/aiproxy/mcp-servers/baidu-map"
 	_ "github.com/labring/aiproxy/mcp-servers/bingcn"
 	_ "github.com/labring/aiproxy/mcp-servers/fetch"
 	_ "github.com/labring/aiproxy/mcp-servers/jina-tools"

+ 7 - 7
openapi-mcp/convert/convert.go

@@ -3,7 +3,6 @@ package convert
 import (
 	"bytes"
 	"context"
-	"encoding/json"
 	"errors"
 	"fmt"
 	"io"
@@ -11,6 +10,7 @@ import (
 	"net/url"
 	"strings"
 
+	"github.com/bytedance/sonic"
 	"github.com/getkin/kin-openapi/openapi3"
 	"github.com/mark3labs/mcp-go/mcp"
 	"github.com/mark3labs/mcp-go/server"
@@ -158,7 +158,7 @@ func newHandler(
 		// Create the request body if needed
 		var reqBody io.Reader
 		if arg.Body != nil {
-			bodyBytes, err := json.Marshal(arg.Body)
+			bodyBytes, err := sonic.Marshal(arg.Body)
 			if err != nil {
 				return nil, fmt.Errorf("failed to marshal request body: %w", err)
 			}
@@ -206,7 +206,7 @@ func newHandler(
 			for key, value := range arg.Forms {
 				switch value := value.(type) {
 				case map[string]any:
-					jsonStr, err := json.Marshal(value)
+					jsonStr, err := sonic.Marshal(value)
 					if err != nil {
 						return nil, err
 					}
@@ -428,18 +428,18 @@ func (c *Converter) generateResponseDescription(responses openapi3.Responses) st
 
 		rawSchema, ok := response.Extensions["schema"].(map[string]any)
 		if ok && len(rawSchema) > 0 {
-			jsonStr, err := json.Marshal(rawSchema)
+			jsonStr, err := sonic.Marshal(rawSchema)
 			if err != nil {
 				continue
 			}
 			schema := openapi3.Schema{}
-			err = json.Unmarshal(jsonStr, &schema)
+			err = schema.UnmarshalJSON(jsonStr)
 			if err != nil {
 				continue
 			}
 
 			property := c.processSchemaProperty(&schema, make(map[string]bool))
-			str, err := json.Marshal(property)
+			str, err := sonic.Marshal(property)
 			if err != nil {
 				continue
 			}
@@ -453,7 +453,7 @@ func (c *Converter) generateResponseDescription(responses openapi3.Responses) st
 						mediaType.Schema.Value,
 						make(map[string]bool),
 					)
-					str, err := json.Marshal(property)
+					str, err := sonic.Marshal(property)
 					if err != nil {
 						continue
 					}

+ 7 - 0
openapi-mcp/go.mod

@@ -3,17 +3,21 @@ module github.com/labring/aiproxy/openapi-mcp
 go 1.24
 
 require (
+	github.com/bytedance/sonic v1.13.2
 	github.com/getkin/kin-openapi v0.132.0
 	github.com/mark3labs/mcp-go v0.30.0
 )
 
 require (
+	github.com/bytedance/sonic/loader v0.2.4 // indirect
+	github.com/cloudwego/base64x v0.1.5 // indirect
 	github.com/go-openapi/jsonpointer v0.21.1 // indirect
 	github.com/go-openapi/swag v0.23.1 // indirect
 	github.com/go-test/deep v1.1.1 // indirect
 	github.com/google/go-cmp v0.7.0 // indirect
 	github.com/google/uuid v1.6.0 // indirect
 	github.com/josharian/intern v1.0.0 // indirect
+	github.com/klauspost/cpuid/v2 v2.2.10 // indirect
 	github.com/mailru/easyjson v0.9.0 // indirect
 	github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
 	github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect
@@ -21,7 +25,10 @@ require (
 	github.com/perimeterx/marshmallow v1.1.5 // indirect
 	github.com/rogpeppe/go-internal v1.14.1 // indirect
 	github.com/spf13/cast v1.8.0 // indirect
+	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
 	github.com/ugorji/go/codec v1.2.13 // indirect
 	github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
+	golang.org/x/arch v0.17.0 // indirect
+	golang.org/x/sys v0.33.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 )

+ 7 - 0
openapi-mcp/go.sum

@@ -1,3 +1,6 @@
+github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ=
+github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
+github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
@@ -16,6 +19,7 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
 github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
+github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
 github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -40,10 +44,13 @@ github.com/spf13/cast v1.8.0 h1:gEN9K4b8Xws4EX0+a0reLmhq8moKn7ntRlQYgjPeCDk=
 github.com/spf13/cast v1.8.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
 github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
 github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
 github.com/ugorji/go/codec v1.2.13 h1:6nvAfJXxwEVFG0UdQwvobVN44a+xQAFiQajSG1Z6bU8=
 github.com/ugorji/go/codec v1.2.13/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
 github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
 github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
+golang.org/x/arch v0.17.0 h1:4O3dfLzd+lQewptAHqjewQZQDyEdejz3VwgeYwkZneU=
+golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=