一文弄懂用Go实现MCP服务

wangzhongyang007 · · 886 次点击 · · 开始浏览    

![](https://files.mdnice.com/user/76962/aee230e8-b66d-466d-8784-6b292f8eb4d3.jpg) 最近这段时间,AI领域里有一个非常热门的概念——**MCP(模型上下文协议)**。Anthropic推出的这一开放标准旨在为大型语言模型和AI助手提供统一的接口,使其能够轻松操作外部工具并完成更复杂的任务。 本文将带你**速览MCP的核心概念,并以Go语言为例,介绍如何开发MCP服务端和客户端**。 ### 为什么MCP如此重要? 在过去,如果想要让AI处理特定的数据,通常只能依赖于预训练数据或者手动上传数据,这既麻烦又低效。即便对于强大的AI模型而言,也存在数据隔离的问题,无法直接访问新的数据源,每次更新数据都需要重新训练或上传。现在,**MCP解决了这个问题,它使得AI不再局限于静态知识库,而是能够像人类一样调用搜索引擎、访问本地文件、连接API服务等,极大提升了AI的动态交互能力**。 ### MCP总体架构 MCP的核心是“**客户端-服务器**”架构,其中MCP客户端可以连接到多个服务器。客户端是指希望通过MCP访问数据的应用程序,如CLI工具、IDE插件或AI应用。 ![](https://files.mdnice.com/user/76962/6abc84c0-d489-4088-b15c-754e3561ff55.png) ### 使用mcp-go构建MCP服务端与客户端 要开始使用Go语言构建MCP项目,首先需要安装`mcp-go`库,这是Go语言实现的Model Context Protocol库,支持LLM应用与外部数据源和工具之间的无缝集成。 ```go go get github.com/mark3labs/mcp-go ``` #### 构建MCP服务端 接下来,我们将演示如何使用`mcp-go`提供的server模块来构建一个通过stdio方式连接的MCP服务器。 1. **创建Server对象** ```go s := server.NewMCPServer("My Server", "1.0.0") ``` 2. **添加工具(Tools)** 例如,我们可以创建一个简单的计算器工具,这次我们实现乘法和除法功能: ```go 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 }) ``` 3. **添加资源(Resources)** 同样地,我们也可以注册一些静态资源,比如README.md文件的内容: ```go 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 }) ``` 4. **启动基于stdio传输类型的服务器** ```go if err := server.ServeStdio(s); err != nil { fmt.Printf("Server error: %v\n", err) } ``` 以上步骤完成后,我们就成功搭建了一个基础的MCP服务器。 #### 构建MCP客户端 接着,我们将展示如何使用`mcp-go`提供的client模块构建一个连接至上述MCP服务器的客户端。 1. **创建MCP客户端** ```go mcpClient, err := client.NewStdioMCPClient("./client/server", []string{}) if err != nil { panic(err) } defer mcpClient.Close() ``` 2. **初始化客户端连接** ```go 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) ``` 3. **调用远程工具** 最后,我们可以通过构造`CallToolRequest`来调用服务器上的工具,如下所示: ```go 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 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) } } ``` **客户端代码:** ```go 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**,备注:面试群。

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

886 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传