最近这段时间,AI领域里有一个非常热门的概念——MCP(模型上下文协议)。Anthropic推出的这一开放标准旨在为大型语言模型和AI助手提供统一的接口,使其能够轻松操作外部工具并完成更复杂的任务。
本文将带你速览MCP的核心概念,并以Go语言为例,介绍如何开发MCP服务端和客户端。
为什么MCP如此重要?
在过去,如果想要让AI处理特定的数据,通常只能依赖于预训练数据或者手动上传数据,这既麻烦又低效。即便对于强大的AI模型而言,也存在数据隔离的问题,无法直接访问新的数据源,每次更新数据都需要重新训练或上传。现在,MCP解决了这个问题,它使得AI不再局限于静态知识库,而是能够像人类一样调用搜索引擎、访问本地文件、连接API服务等,极大提升了AI的动态交互能力。
MCP总体架构
MCP的核心是“客户端-服务器”架构,其中MCP客户端可以连接到多个服务器。客户端是指希望通过MCP访问数据的应用程序,如CLI工具、IDE插件或AI应用。
使用mcp-go构建MCP服务端与客户端
要开始使用Go语言构建MCP项目,首先需要安装mcp-go
库,这是Go语言实现的Model Context Protocol库,支持LLM应用与外部数据源和工具之间的无缝集成。
go get github.com/mark3labs/mcp-go
构建MCP服务端
接下来,我们将演示如何使用mcp-go
提供的server模块来构建一个通过stdio方式连接的MCP服务器。
- 创建Server对象
s := server.NewMCPServer("My Server", "1.0.0")
- 添加工具(Tools)
例如,我们可以创建一个简单的计算器工具,这次我们实现乘法和除法功能:
calculatorTool := mcp.NewTool("calculate",
mcp.WithDescription("执行基本的算术运算"),
mcp.WithString("operation",
mcp.Required(),
mcp.Description("要执行的算术运算类型"),
mcp.Enum("multiply", "divide"), // 修改为仅支持乘法和除法
),
mcp.WithNumber("x",
mcp.Required(),
mcp.Description("第一个数字"),
),
mcp.WithNumber("y",
mcp.Required(),
mcp.Description("第二个数字"),
),
)
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 "multiply":
result = x * y
case "divide":
if y == 0 {
return nil, errors.New("不允许除以零")
}
result = x / y
}
return mcp.FormatNumberResult(result), nil
})
- 添加资源(Resources)
同样地,我们也可以注册一些静态资源,比如README.md文件的内容:
resource := mcp.NewResource(
"docs://readme",
"项目说明文档",
mcp.WithResourceDescription("项目的 README 文件"),
mcp.WithMIMEType("text/markdown"),
)
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
})
- 启动基于stdio传输类型的服务器
if err := server.ServeStdio(s); err != nil {
fmt.Printf("Server error: %v\n", err)
}
以上步骤完成后,我们就成功搭建了一个基础的MCP服务器。
构建MCP客户端
接着,我们将展示如何使用mcp-go
提供的client模块构建一个连接至上述MCP服务器的客户端。
- 创建MCP客户端
mcpClient, err := client.NewStdioMCPClient("./client/server", []string{})
if err != nil {
panic(err)
}
defer mcpClient.Close()
- 初始化客户端连接
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
initRequest := mcp.InitializeRequest{}
initRequest.Params.ProtocolVersion = mcp.LATEST_PROTOCOL_VERSION
initRequest.Params.ClientInfo = mcp.Implementation{
Name: "Client Demo",
Version: "1.0.0",
}
initResult, err := mcpClient.Initialize(ctx, initRequest)
if err != nil {
panic(err)
}
fmt.Printf("初始化成功,服务器信息: %s %s\n", initResult.ServerInfo.Name, initResult.ServerInfo.Version)
- 调用远程工具
最后,我们可以通过构造CallToolRequest
来调用服务器上的工具,如下所示:
toolRequest := mcp.CallToolRequest{
Request: mcp.Request{
Method: "tools/call",
},
}
toolRequest.Params.Name = "calculate"
toolRequest.Params.Arguments = map[string]any{
"operation": "multiply", // 调用乘法
"x": 2,
"y": 3,
}
result, err := mcpClient.CallTool(ctx, toolRequest)
if err != nil {
panic(err)
}
fmt.Println("调用工具结果:", result.Content[0].(mcp.TextContent).Text)
完整代码示例
以下是完整的代码示例,包括服务端和客户端的实现:
服务端代码:
package main
import (
"context"
"errors"
"fmt"
"os"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
func main() {
s := server.NewMCPServer("Server Demo", "1.0.0")
// 添加工具
calculatorTool := mcp.NewTool("calculate",
mcp.WithDescription("执行基本的算术运算"),
mcp.WithString("operation",
mcp.Required(),
mcp.Description("要执行的算术运算类型"),
mcp.Enum("multiply", "divide"),
),
mcp.WithNumber("x",
mcp.Required(),
mcp.Description("第一个数字"),
),
mcp.WithNumber("y",
mcp.Required(),
mcp.Description("第二个数字"),
),
)
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 "multiply":
result = x * y
case "divide":
if y == 0 {
return nil, errors.New("不允许除以零")
}
result = x / y
}
return mcp.FormatNumberResult(result), nil
})
// 启动基于 stdio 的服务器
if err := server.ServeStdio(s); err != nil {
fmt.Printf("Server error: %v\n", err)
}
}
客户端代码:
package main
import (
"context"
"fmt"
"time"
"github.com/mark3labs/mcp-go/client"
"github.com/mark3labs/mcp-go/mcp"
)
func main() {
mcpClient, err := client.NewStdioMCPClient("./client/server", []string{})
if err != nil {
panic(err)
}
defer mcpClient.Close()
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
initRequest := mcp.InitializeRequest{}
initRequest.Params.ProtocolVersion = mcp.LATEST_PROTOCOL_VERSION
initRequest.Params.ClientInfo = mcp.Implementation{
Name: "Client Demo",
Version: "1.0.0",
}
initResult, err := mcpClient.Initialize(ctx, initRequest)
if err != nil {
panic(err)
}
fmt.Printf("初始化成功,服务器信息: %s %s\n", initResult.ServerInfo.Name, initResult.ServerInfo.Version)
// 调用工具
toolRequest := mcp.CallToolRequest{
Request: mcp.Request{
Method: "tools/call",
},
}
toolRequest.Params.Name = "calculate"
toolRequest.Params.Arguments = map[string]any{
"operation": "multiply",
"x": 2,
"y": 3,
}
result, err := mcpClient.CallTool(ctx, toolRequest)
if err != nil {
panic(err)
}
fmt.Println("调用工具结果:", result.Content[0].(mcp.TextContent).Text)
}
希望这篇文章能帮助你快速入门Go语言下的MCP开发!
欢迎关注 ❤
我们搞了一个免费的面试真题共享群,互通有无,一起刷题进步。
没准能让你能刷到自己意向公司的最新面试题呢。
感兴趣的朋友们可以加我微信:wangzhongyang1993,备注:面试群。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。