
不多废话,假定你已经了解了 MCP (Model Context Protocol)的背景知识。
当前 Go 生态圈有两个比较知名的开发 MCP 的库,一个是mark3labs/mcp-go[1],另一个是metoro-io/mcp-golang[2],两个使用起来都很简单,这次我们根据 star 数量选择第一个做演示。
mcp-go 入门
mark3labs/mcp-go 库提供了功能丰富的例子,而且 README 也给出了 MCP Tool 每种类型的示例。
启动服务
MCP 支持 SSE 和标准输入输出两种类型,一般我们在自己的机器上常用标准输入输出传输方式:
// Create a basic server
s := server.NewMCPServer(
"My Server", // Server name
"1.0.0", // Version
)
// Start the server using stdio
if err := server.ServeStdio(s); err != nil {
log.Fatalf("Server error: %v", err)
}
增加资源
资源是你向 LLMs 暴露数据的方式。它们可以是任何东西:文件、API 响应、数据库查询、系统信息等。资源可以是:
静态资源(固定 URI) 动态资源(使用 URI 模板)
这是一个静态资源的简单示例:
// Static resource example - exposing a README file
resource := mcp.NewResource(
"docs://readme",
"Project README",
mcp.WithResourceDescription("The project"s README file"),
mcp.WithMIMEType("text/markdown"),
)
// Add resource with its handler
s.AddResource(resource, func(ctx context.Context, request mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) {
content, err := os.ReadFile("README.md")
if err != nil {
return nil, err
}
return []mcp.ResourceContents{
mcp.TextResourceContents{
URI: "docs://readme",
MIMEType: "text/markdown",
Text: string(content),
},
}, nil
})
增加工具
工具让 LLM 通过你的服务器执行操作。与资源不同,工具预期会进行计算并产生副作用。它们类似于 REST API 中的 POST 端点。 下面是一个算术运算的工具示例:
calculatorTool := mcp.NewTool("calculate",
mcp.WithDescription("Perform basic arithmetic calculations"),
mcp.WithString("operation",
mcp.Required(),
mcp.Description("The arithmetic operation to perform"),
mcp.Enum("add", "subtract", "multiply", "divide"),
),
mcp.WithNumber("x",
mcp.Required(),
mcp.Description("First number"),
),
mcp.WithNumber("y",
mcp.Required(),
mcp.Description("Second number"),
),
)
s.AddTool(calculatorTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
op := request.Params.Arguments["operation"].(string)
x := request.Params.Arguments["x"].(float64)
y := request.Params.Arguments["y"].(float64)
var result float64
switch op {
case "add":
result = x + y
case "subtract":
result = x - y
case "multiply":
result = x * y
case "divide":
if y == 0 {
return nil, errors.New("Division by zero is not allowed")
}
result = x / y
}
return mcp.FormatNumberResult(result), nil
})
工具可以用于任何种类的计算:
Database queries 数据库查询 File operations 文件操作 External API calls 外部 API 调用 Calculations 计算 System operations 系统操作
每个工具应该:
有清晰的描述 验证输入 优雅处理错误 返回结构化的响应 使用适当的结果类型
增加提示
下面是一个简单的提示词示例,它需要一个名称参数,然后返回一个问候提示词:
// Simple greeting prompt
s.AddPrompt(mcp.NewPrompt("greeting",
mcp.WithPromptDescription("A friendly greeting prompt"),
mcp.WithArgument("name",
mcp.ArgumentDescription("Name of the person to greet"),
),
), func(ctx context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) {
name := request.Params.Arguments["name"]
if name == "" {
name = "friend"
}
return mcp.NewGetPromptResult(
"A friendly greeting",
[]mcp.PromptMessage{
mcp.NewPromptMessage(
mcp.RoleAssistant,
mcp.NewTextContent(fmt.Sprintf("Hello, %s! How can I help you today?", name)),
),
},
), nil
})
实战:实现一个查询 IP 地理信息的 MCP Tool
首先我们先实现这个 MCP Tool main 函数的主逻辑:
package main
import (
"context"
"errors"
"fmt"
"io"
"net"
"net/http"
"github.com/kataras/golog"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
func main() {
// Create MCP server
s := server.NewMCPServer(
"ip-mcp",
"1.0.0",
)
// Add tool
tool := mcp.NewTool("ip_query",
mcp.WithDescription("query geo location of an IP address"),
mcp.WithString("ip",
mcp.Required(),
mcp.Description("IP address to query"),
),
)
// Add tool handler
s.AddTool(tool, ipQueryHandler)
// Start the stdio server
if err := server.ServeStdio(s); err != nil {
fmt.Printf("Server error: %v\n", err)
}
}
这里我们增加了一个ip_query
查询 IP 信息的工具,描述清楚它的功能是查询 IP 地址的地理位置。 这个工具需要一个参数:待查询的 IP 地址。 然后这个工具具体的实现是ipQueryHandler
函数。 这个 MCP Server 使用标准输入输出传输。
接下来就是ipQueryHandler
函数实现了。先前我实现了一个 IP 地址查询的网站 所以这里我调用它的 API 进行封装就很简单了。
我将请求的数据以文本的方式返回 MCP Client。 因为这个数据是 JSON 格式,所以 MCP Client 会得到一个 JSON 格式的文本。
func ipQueryHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
ip, ok := request.Params.Arguments["ip"].(string)
if !ok {
return nil, errors.New("ip must be a string")
}
parsedIP := net.ParseIP(ip)
if parsedIP == nil {
golog.Errorf("invalid IP address: %s", ip)
return nil, errors.New("invalid IP address")
}
resp, err := http.Get("" + ip)
if err != nil {
golog.Errorf("Error fetching IP information: %v", err)
return nil, fmt.Errorf("Error fetching IP information: %v", err)
}
defer resp.Body.Close()
data, err := io.ReadAll(resp.Body)
if err != nil {
golog.Errorf("Error reading response body: %v", err)
return nil, fmt.Errorf("Error reading response body: %v", err)
}
return mcp.NewToolResultText(string(data)), nil
}
注意检查参数的合法性,处理程序中的 error。在 2025 年的今天,这些琐碎的事都可以由代码辅助工具如 github copilot 自动完成,我们一路回车即可。
编译这个程序为可执行二进制:

你可以把这个程序放在系统目录中方便执行,也可以不放在系统目录中,后面我们配置这个 mcp server 的时候会用到它的路径。
测试 MCP Server
我们使用deepchat[3]来测试我们开发的这个 IP 查询的 MCP 工具。 deepchat 已经支持 MCP 了,而且配置起来也非常简单。
在 MCP 配置界面,我们增加一个 MCP Server,配置如下(注意:ip-mcp 工具的实际路径):

然后启动它:

这个时候你应该在对话框窗口看到运行着的 MCP Tool,包括我们的 IP 查询工具:
对话框中测试一把:

测试正常。看起来 deepchat 还会把工具的返回结果交给 deepseek 处理。
这样,我们就简单了实现了一个 MCP Server 并测试成功。可以看到我们可以对既有的 API 或者产品很快的进行封装成 MCP 工具,这也是 baidu 地图、高德地图能够很快推出 MCP Server 的原因。
发表评论 取消回复