Add all in one mcp (#1978)

This commit is contained in:
澄潭
2025-03-30 00:25:21 +08:00
committed by GitHub
parent 037c71a320
commit 492c5d350a
14 changed files with 318 additions and 75 deletions

View File

@@ -128,6 +128,56 @@ func (t MyTool) Call(ctx server.HttpContext, s server.Server) error {
}
```
## Tool Loading
For better organization, you can create a separate file to load all your tools:
```go
// tools/load_tools.go
package tools
import (
"github.com/alibaba/higress/plugins/wasm-go/pkg/mcp"
"github.com/alibaba/higress/plugins/wasm-go/pkg/mcp/server"
)
func LoadTools(server *mcp.MCPServer) server.Server {
return server.AddMCPTool("my_tool", &MyTool{}).
AddMCPTool("another_tool", &AnotherTool{})
// Add more tools as needed
}
```
This approach to organizing code facilitates integration with the all-in-one MCP server plugin. The all-in-one plugin combines multiple MCP servers into a single plugin, reducing the overhead of deploying multiple plugins on the gateway.
### All-in-One Integration
The all-in-one plugin packages multiple MCP servers into a single WASM binary. Each MCP server maintains its own identity and configuration, but they share the same plugin instance. Here's an example of how multiple MCP servers are integrated in the all-in-one plugin:
```go
// all-in-one/main.go
package main
import (
amap "amap-tools/tools"
quark "quark-search/tools"
"github.com/alibaba/higress/plugins/wasm-go/pkg/mcp"
)
func main() {}
func init() {
mcp.LoadMCPServer(mcp.AddMCPServer("quark-search",
quark.LoadTools(&mcp.MCPServer{})))
mcp.LoadMCPServer(mcp.AddMCPServer("amap-tools",
amap.LoadTools(&mcp.MCPServer{})))
mcp.InitMCPServer()
}
```
The configuration for the all-in-one plugin follows the same pattern as individual MCP server plugins. The `name` field in the server configuration is used to identify and route requests to the appropriate MCP server within the all-in-one plugin.
## Main Entry Point
The main.go file is the entry point for your MCP server. It registers your tools and resources:
@@ -139,21 +189,37 @@ package main
import (
"my-mcp-server/tools"
"github.com/alibaba/higress/plugins/wasm-go/pkg/mcp/server"
"github.com/alibaba/higress/plugins/wasm-go/pkg/mcp"
)
func main() {}
func init() {
myMCPServer := &server.MCPServer{}
server.Load(server.AddMCPServer(
"my-mcp-server", // Server name
myMCPServer.AddMCPTool("my_tool", &tools.MyTool{}), // Register tools
// Add more tools as needed
))
mcp.LoadMCPServer(mcp.AddMCPServer("my-mcp-server",
tools.LoadTools(&mcp.MCPServer{})))
mcp.InitMCPServer()
}
```
## Plugin Configuration
When deploying your MCP server as a Higress plugin, you need to configure it in the Higress configuration. Here's an example configuration:
```yaml
server:
# MCP server name - MUST match the name used in mcp.AddMCPServer() in your code
name: my-mcp-server
# MCP server configuration
config:
apiKey: your-api-key-here
# Optional: If configured, acts as a whitelist - only tools listed here can be called
tools:
- my_tool
- another_tool
```
> **Important**: The `name` field in the server configuration must exactly match the server name used in the `mcp.AddMCPServer()` call in your code. This is how the system identifies which MCP server should handle the request.
## Dependencies
Your MCP server must use a specific version of the wasm-go SDK that supports Go 1.24's WebAssembly compilation features:

View File

@@ -125,6 +125,56 @@ func (t MyTool) Call(ctx server.HttpContext, s server.Server) error {
}
```
## 工具加载
为了更好地组织代码,您可以创建一个单独的文件来加载所有工具:
```go
// tools/load_tools.go
package tools
import (
"github.com/alibaba/higress/plugins/wasm-go/pkg/mcp"
"github.com/alibaba/higress/plugins/wasm-go/pkg/mcp/server"
)
func LoadTools(server *mcp.MCPServer) server.Server {
return server.AddMCPTool("my_tool", &MyTool{}).
AddMCPTool("another_tool", &AnotherTool{})
// 根据需要添加更多工具
}
```
以这种方式组织代码,可以方便被 all-in-one 目录下的 MCP server 插件集成。all-in-one 插件将所有 MCP server 的逻辑打包到一个插件里,从而降低网关上部署多个插件带来的额外开销。
### All-in-One 集成
all-in-one 插件将多个 MCP server 打包到一个 WASM 二进制文件中。每个 MCP server 保持自己的身份和配置,但它们共享同一个插件实例。以下是 all-in-one 插件中集成多个 MCP server 的示例:
```go
// all-in-one/main.go
package main
import (
amap "amap-tools/tools"
quark "quark-search/tools"
"github.com/alibaba/higress/plugins/wasm-go/pkg/mcp"
)
func main() {}
func init() {
mcp.LoadMCPServer(mcp.AddMCPServer("quark-search",
quark.LoadTools(&mcp.MCPServer{})))
mcp.LoadMCPServer(mcp.AddMCPServer("amap-tools",
amap.LoadTools(&mcp.MCPServer{})))
mcp.InitMCPServer()
}
```
all-in-one 插件的配置方式与所有 MCP server 插件都是一样的,都是通过 server 配置中的 name 字段来找到对应的 MCP server。
## 主入口点
main.go 文件是 MCP 服务器的入口点。它注册工具和资源:
@@ -136,21 +186,37 @@ package main
import (
"my-mcp-server/tools"
"github.com/alibaba/higress/plugins/wasm-go/pkg/mcp/server"
"github.com/alibaba/higress/plugins/wasm-go/pkg/mcp"
)
func main() {}
func init() {
myMCPServer := &server.MCPServer{}
server.Load(server.AddMCPServer(
"my-mcp-server", // 服务器名称
myMCPServer.AddMCPTool("my_tool", &tools.MyTool{}), // 注册工具
// 根据需要添加更多工具
))
mcp.LoadMCPServer(mcp.AddMCPServer("my-mcp-server",
tools.LoadTools(&mcp.MCPServer{})))
mcp.InitMCPServer()
}
```
## 插件配置
当将您的 MCP 服务器部署为 Higress 插件时,您需要在 Higress 配置中进行配置。以下是一个示例配置:
```yaml
server:
# MCP 服务器名称 - 必须与代码中 mcp.AddMCPServer() 调用时使用的名称完全一致
name: my-mcp-server
# MCP 服务器配置
config:
apiKey: 您的API密钥
# 可选:如果配置了,则起到白名单作用 - 只有列在这里的工具才能被调用
tools:
- my_tool
- another_tool
```
> **重要提示**server 配置中的 `name` 字段必须与代码中 `mcp.AddMCPServer()` 调用时使用的服务器名称完全一致。系统通过这个名称来识别应该由哪个 MCP 服务器处理请求。
## 依赖项
您的 MCP 服务器必须使用支持 Go 1.24 WebAssembly 编译功能的特定版本的 wasm-go SDK

View File

@@ -0,0 +1,29 @@
module all-in-one
go 1.24.1
replace quark-search => ../quark-search
replace amap-tools => ../amap-tools
require (
amap-tools v0.0.0-00010101000000-000000000000
github.com/alibaba/higress/plugins/wasm-go v1.4.4-0.20250329145934-61b36a20cd9c
quark-search v0.0.0-00010101000000-000000000000
)
require (
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20250323151219-d75620c61711 // indirect
github.com/invopop/jsonschema v0.13.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/tidwall/gjson v1.17.3 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
github.com/tidwall/resp v0.1.1 // indirect
github.com/tidwall/sjson v1.2.5 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View File

@@ -0,0 +1,38 @@
github.com/alibaba/higress/plugins/wasm-go v1.4.4-0.20250329145934-61b36a20cd9c h1:giBV8e7p0qxRdBsCu4AjXbpUI8sNiGkEtPNWEXf6fA4=
github.com/alibaba/higress/plugins/wasm-go v1.4.4-0.20250329145934-61b36a20cd9c/go.mod h1:csP9Mpkc+gVgbZsizCdcYSy0LJrQA+//RcnZBInyknc=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
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/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20250323151219-d75620c61711 h1:n5sZwSZWQ5uKS69hu50/0gliTFrIJ1w+g/FSdIIiZIs=
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20250323151219-d75620c61711/go.mod h1:tRI2LfMudSkKHhyv1uex3BWzcice2s/l8Ah8axporfA=
github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.17.3 h1:bwWLZU7icoKRG+C+0PNwIKC6FCJO/Q3p2pZvuP0jN94=
github.com/tidwall/gjson v1.17.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/resp v0.1.1 h1:Ly20wkhqKTmDUPlyM1S7pWo5kk0tDu8OoC/vFArXmwE=
github.com/tidwall/resp v0.1.1/go.mod h1:3/FrruOBAxPTPtundW0VXgmsQ4ZBA0Aw714lVYgwFa0=
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -0,0 +1,32 @@
// Copyright (c) 2022 Alibaba Group Holding Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
amap "amap-tools/tools"
quark "quark-search/tools"
"github.com/alibaba/higress/plugins/wasm-go/pkg/mcp"
)
func main() {}
func init() {
mcp.LoadMCPServer(mcp.AddMCPServer("quark-search",
quark.LoadTools(&mcp.MCPServer{})))
mcp.LoadMCPServer(mcp.AddMCPServer("amap-tools",
amap.LoadTools(&mcp.MCPServer{})))
mcp.InitMCPServer()
}

View File

@@ -5,7 +5,7 @@ go 1.24
toolchain go1.24.1
require (
github.com/alibaba/higress/plugins/wasm-go v1.4.4-0.20250329122411-e07be585ff55
github.com/alibaba/higress/plugins/wasm-go v1.4.4-0.20250329145934-61b36a20cd9c
github.com/tidwall/gjson v1.17.3
)

View File

@@ -1,5 +1,5 @@
github.com/alibaba/higress/plugins/wasm-go v1.4.4-0.20250329122411-e07be585ff55 h1:yGPhs3VhC4Mj4SmbaLcKSZ1sV8wRW/eGf/13P0c2+S8=
github.com/alibaba/higress/plugins/wasm-go v1.4.4-0.20250329122411-e07be585ff55/go.mod h1:csP9Mpkc+gVgbZsizCdcYSy0LJrQA+//RcnZBInyknc=
github.com/alibaba/higress/plugins/wasm-go v1.4.4-0.20250329145934-61b36a20cd9c h1:giBV8e7p0qxRdBsCu4AjXbpUI8sNiGkEtPNWEXf6fA4=
github.com/alibaba/higress/plugins/wasm-go v1.4.4-0.20250329145934-61b36a20cd9c/go.mod h1:csP9Mpkc+gVgbZsizCdcYSy0LJrQA+//RcnZBInyknc=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=

View File

@@ -17,26 +17,13 @@ package main
import (
"amap-tools/tools"
"github.com/alibaba/higress/plugins/wasm-go/pkg/mcp/server"
"github.com/alibaba/higress/plugins/wasm-go/pkg/mcp"
)
func main() {}
func init() {
amapServer := &server.MCPServer{}
server.Load(server.AddMCPServer(
"amap-tools",
amapServer.AddMCPTool("maps_geo", &tools.GeoRequest{}).
AddMCPTool("maps_bicycling", &tools.BicyclingRequest{}).
AddMCPTool("maps_direction_transit_integrated", &tools.TransitIntegratedRequest{}).
AddMCPTool("maps_ip_location", &tools.IPLocationRequest{}).
AddMCPTool("maps_weather", &tools.WeatherRequest{}).
AddMCPTool("maps_direction_driving", &tools.DrivingRequest{}).
AddMCPTool("maps_around_search", &tools.AroundSearchRequest{}).
AddMCPTool("maps_search_detail", &tools.SearchDetailRequest{}).
AddMCPTool("maps_regeocode", &tools.ReGeocodeRequest{}).
AddMCPTool("maps_text_search", &tools.TextSearchRequest{}).
AddMCPTool("maps_distance", &tools.DistanceRequest{}).
AddMCPTool("maps_direction_walking", &tools.WalkingRequest{}),
))
mcp.LoadMCPServer(mcp.AddMCPServer("amap-tools",
tools.LoadTools(&mcp.MCPServer{})))
mcp.InitMCPServer()
}

View File

@@ -1,33 +0,0 @@
// server/server.go
package server
import (
"encoding/json"
"errors"
"github.com/alibaba/higress/plugins/wasm-go/pkg/wrapper"
)
// Define your server configuration structure
type AmapMCPServer struct {
ApiKey string `json:"apiKey"`
// Add other configuration fields as needed
}
// Validate the configuration
func (s AmapMCPServer) ConfigHasError() error {
if s.ApiKey == "" {
return errors.New("missing api key")
}
return nil
}
// Parse configuration from JSON
func ParseFromConfig(configBytes []byte, server *AmapMCPServer) error {
return json.Unmarshal(configBytes, server)
}
// Parse configuration from HTTP request
func ParseFromRequest(ctx wrapper.HttpContext, server *AmapMCPServer) error {
return ctx.ParseMCPServerConfig(server)
}

View File

@@ -0,0 +1,35 @@
// Copyright (c) 2022 Alibaba Group Holding Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package tools
import (
"github.com/alibaba/higress/plugins/wasm-go/pkg/mcp"
"github.com/alibaba/higress/plugins/wasm-go/pkg/mcp/server"
)
func LoadTools(server *mcp.MCPServer) server.Server {
return server.AddMCPTool("maps_geo", &GeoRequest{}).
AddMCPTool("maps_bicycling", &BicyclingRequest{}).
AddMCPTool("maps_direction_transit_integrated", &TransitIntegratedRequest{}).
AddMCPTool("maps_ip_location", &IPLocationRequest{}).
AddMCPTool("maps_weather", &WeatherRequest{}).
AddMCPTool("maps_direction_driving", &DrivingRequest{}).
AddMCPTool("maps_around_search", &AroundSearchRequest{}).
AddMCPTool("maps_search_detail", &SearchDetailRequest{}).
AddMCPTool("maps_regeocode", &ReGeocodeRequest{}).
AddMCPTool("maps_text_search", &TextSearchRequest{}).
AddMCPTool("maps_distance", &DistanceRequest{}).
AddMCPTool("maps_direction_walking", &WalkingRequest{})
}

View File

@@ -5,7 +5,7 @@ go 1.24
toolchain go1.24.1
require (
github.com/alibaba/higress/plugins/wasm-go v1.4.4-0.20250329122411-e07be585ff55
github.com/alibaba/higress/plugins/wasm-go v1.4.4-0.20250329145934-61b36a20cd9c
github.com/tidwall/gjson v1.17.3
)

View File

@@ -1,5 +1,5 @@
github.com/alibaba/higress/plugins/wasm-go v1.4.4-0.20250329122411-e07be585ff55 h1:yGPhs3VhC4Mj4SmbaLcKSZ1sV8wRW/eGf/13P0c2+S8=
github.com/alibaba/higress/plugins/wasm-go v1.4.4-0.20250329122411-e07be585ff55/go.mod h1:csP9Mpkc+gVgbZsizCdcYSy0LJrQA+//RcnZBInyknc=
github.com/alibaba/higress/plugins/wasm-go v1.4.4-0.20250329145934-61b36a20cd9c h1:giBV8e7p0qxRdBsCu4AjXbpUI8sNiGkEtPNWEXf6fA4=
github.com/alibaba/higress/plugins/wasm-go v1.4.4-0.20250329145934-61b36a20cd9c/go.mod h1:csP9Mpkc+gVgbZsizCdcYSy0LJrQA+//RcnZBInyknc=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=

View File

@@ -17,14 +17,13 @@ package main
import (
"quark-search/tools"
"github.com/alibaba/higress/plugins/wasm-go/pkg/mcp/server"
"github.com/alibaba/higress/plugins/wasm-go/pkg/mcp"
)
func main() {}
func init() {
quarkSearchServer := &server.MCPServer{}
server.Load(server.AddMCPServer(
"quark-search",
quarkSearchServer.AddMCPTool("web_search", &tools.WebSearch{})))
mcp.LoadMCPServer(mcp.AddMCPServer("quark-search",
tools.LoadTools(&mcp.MCPServer{})))
mcp.InitMCPServer()
}

View File

@@ -0,0 +1,24 @@
// Copyright (c) 2022 Alibaba Group Holding Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package tools
import (
"github.com/alibaba/higress/plugins/wasm-go/pkg/mcp"
"github.com/alibaba/higress/plugins/wasm-go/pkg/mcp/server"
)
func LoadTools(server *mcp.MCPServer) server.Server {
return server.AddMCPTool("web_search", &WebSearch{})
}