分享
  1. 首页
  2. 主题
  3. Go语言

GFast开发MCP服务器之mark3labs/mcp-go库接入(一)

kocie · · 804 次点击 · 开始浏览 置顶
这是一个创建于 的主题,其中的信息可能已经有所发展或是发生改变。

这将是一个系列文章,我们将从简到繁开发一套基于GFast框架下MCP服务工具 github.com/mark3labs/mcp-go介绍 github.com/mark3labs/mcp-go 是一个基于 Go 语言实现的 Model Context Protocol (MCP) 的开源项目,旨在为大语言模型(LLM)与外部系统的交互提供标准化协议支持。以下是综合搜索结果的关键信息: 1. 项目功能与定位 该项目实现了 MCP 协议的完整规范,提供 客户端和服务端能力,用于 LLM 与数据资源、工具的集成。例如,支持通过标准化方式暴露资源、提示词和工具,便于 LLM 调用外部 API 或执行操作。 目前支持 stdio 和 SSE(Server-Sent Events) 作为传输协议,未来可能扩展其他通信方式(如 WebSocket 或 gRPC)。 2. 技术特点 Go 语言优势:与 Python 或 TypeScript 实现的 MCP SDK 相比,Go 版本强调强类型检查、高并发性能和简易部署,适合生产环境的高可靠性需求。 模块化设计:分层架构(传输层、协议层、用户层)确保扩展性和维护性,开发者可自定义传输方式或协议扩展。 3. 与其他 MCP 实现的对比 ThinkInAI 的 Go-MCP(另一个 Go 实现)更注重生态建设(如 Marketplace 和工具链),而 mark3labs/mcp-go 目前功能更聚焦于协议基础实现。 Python/TypeScript SDK 动态语言的灵活性更高,但长期维护成本可能更高。 4. 安全与维护状态 根据 GitHub 页面,该项目 未设置安全策略文件(SECURITY.md),且无公开的安全公告,需注意潜在风险。 项目活跃度未明确提及,但代码示例和协议支持显示其具备实际可用性。 总结 mark3labs/mcp-go 是一个轻量级的 MCP 协议 Go 实现,适合需要高性能、强类型支持的 LLM 集成场景。若需更完整的生态工具(如服务发现、调试面板),可参考 ThinkInAI 的 Go-MCP 项目。建议进一步查阅其 GitHub 仓库的文档和示例以评估适用性。 MCP Server 的业务能力 Request Method 发起方 响应方 描述 initialized Client Server 初始化会话 tools-list Client Server 发现可用的工具 tools/call Client Server 调用工具 resources/list Client Server 发现可用的资源 resources/read Client Server 获取资源内容 resources/templates Client Server 发现可用的参数化资源 resources/subscribe Client Server 订阅特定资源,监听其变化事件 prompts/list Client Server 发现可用的提示词 prompts/get Client Server 获取特定提示词 roots/list Server Client 列出服务器有权访问的客户端文件系统根节点(暴露目录和文件) sampling/create Server Client 启用服务器的 AI 生成能力( sampling creation ) 一、简单的MCP服务器demo实现(stdio方式) package main import ( "context" "errors" "fmt" "github.com/mark3labs/mcp-go/mcp" "github.com/mark3labs/mcp-go/server" ) func main() { // 创建MCP服务器 s := server.NewMCPServer( "Demo 🚀", // 服务器名称 "1.0.0", // 服务器版本 ) // 添加工具 tool := mcp.NewTool("hello_world", // 工具名称 mcp.WithDescription("Say hello to someone"), // 工具描述 mcp.WithString("name", // 参数名称 mcp.Required(), // 参数是必需的 mcp.Description("Name of the person to greet"), // 参数描述 ), ) // 为工具添加处理器 s.AddTool(tool, helloHandler) // 启动标准输入输出服务器 if err := server.ServeStdio(s); err != nil { fmt.Printf("Server error: %v\n", err) // 打印服务器错误 } } func helloHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { // 从请求参数中获取名字参数,并断言为字符串类型 name, ok := request.Params.Arguments["name"].(string) if !ok { // 如果断言失败,返回错误 return nil, errors.New("name must be a string") } // 返回包含问候语的结果 return mcp.NewToolResultText(fmt.Sprintf("Hello, %s!", name)), nil } 这段代码实现了一个简单的 MCP(Model Context Protocol)服务器,它通过标准输入/输出(stdio)与客户端交互,提供了一个名为 hello_world 的工具,用于向指定的人打招呼。以下是详细解析: 1. 核心功能 工具注册:服务器注册了一个工具 hello_world,接收一个字符串参数 name,返回问候语。 标准输入/输出通信:通过 stdio 与客户端交互(适合命令行或管道调用)。 错误处理:验证参数类型,返回明确的错误信息。 2. 代码逐层解析 (1) 初始化 MCP 服务器 s := server.NewMCPServer( "Demo 🚀", // 服务器名称(显示标识) "1.0.0", // 服务器版本 ) 创建一个 MCP 服务器实例,指定名称和版本(用于元信息标识)。 (2) 定义工具 hello_world tool := mcp.NewTool("hello_world", mcp.WithDescription("Say hello to someone"), // 工具功能描述 mcp.WithString("name", // 参数名 mcp.Required(), // 参数必填 mcp.Description("Name of the person to greet"), // 参数描述 ), ) 工具名称:hello_world。 参数配置: name:字符串类型,必填字段,描述为"Name of the person to greet"。 通过 mcp.WithString 和链式方法声明参数约束。 (3) 注册工具处理器 s.AddTool(tool, helloHandler) 将工具 hello_world 绑定到处理函数 helloHandler。 (4) 启动服务器 server.ServeStdio(s) 启动标准输入/输出模式,监听来自客户端的请求。 (5) 工具处理函数 helloHandler func helloHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { name, ok := request.Params.Arguments["name"].(string) // 获取参数 if !ok { return nil, errors.New("name must be a string") // 类型检查 } return mcp.NewToolResultText(fmt.Sprintf("Hello, %s!", name)), nil // 返回结果 } 参数提取:从 request.Params.Arguments 中获取 name 参数,并验证是否为字符串。 错误处理:若类型不符,返回错误。 结果生成:使用 mcp.NewToolResultText 封装文本响应。 3. 数据流示例 客户端请求(JSON 输入) { "tool": "hello_world", "params": { "arguments": { "name": "Alice" } } } 服务器响应 { "result": { "text": "Hello, Alice!" } } 错误场景(非字符串参数) { "tool": "hello_world", "params": { "arguments": { "name": 123 } } } 响应错误: name must be a string 4. 关键设计点 工具化架构 每个功能(如问候)封装为独立工具,通过名称调用,支持灵活扩展。 强类型校验 使用 Go 的类型断言确保参数合法性。 标准化协议 输入/输出遵循 MCP 格式,兼容不同客户端(如 CLI、Web 前端)。 轻量级通信 stdio 模式无需网络依赖,适合集成到脚本或管道。 5. 如何运行? 编译服务器: $ go build -v -o server 再启动 mcp inspetor:(通过标准输入): $ npx -y @modelcontextprotocol/inspector ./server 运行结果: 20250414234917 点击connect: 20250414235059 连接后可以列出相关工具,此时显示hello_world,可以进行run tool测试 二、简单的MCP服务器demo实现(同时支持stdio和SSE方式) package main import ( "context" "encoding/json" "flag" "fmt" "io" "log" "net/http" "os" "github.com/mark3labs/mcp-go/mcp" "github.com/mark3labs/mcp-go/server" ) // authKey 是用于在context中存储认证令牌的自定义键类型 type authKey struct{} // withAuthKey 将认证令牌添加到context中 // ctx: 原始context // auth: 要存储的认证令牌 // 返回: 包含令牌的新context func withAuthKey(ctx context.Context, auth string) context.Context { return context.WithValue(ctx, authKey{}, auth) } // authFromRequest 从HTTP请求头中提取认证令牌并存入context // ctx: 原始context // r: HTTP请求对象 // 返回: 包含认证令牌的新context func authFromRequest(ctx context.Context, r *http.Request) context.Context { return withAuthKey(ctx, r.Header.Get("Authorization")) } // authFromEnv 从环境变量中提取认证令牌并存入context // ctx: 原始context // 返回: 包含认证令牌的新context func authFromEnv(ctx context.Context) context.Context { return withAuthKey(ctx, os.Getenv("API_KEY")) } // tokenFromContext 从context中提取认证令牌 // ctx: 包含令牌的context // 返回: 令牌字符串或错误(如果令牌不存在或类型不符) // 注意: 此方法不关心令牌来源(HTTP头或环境变量),统一通过context获取 func tokenFromContext(ctx context.Context) (string, error) { auth, ok := ctx.Value(authKey{}).(string) if !ok { return "", fmt.Errorf("missing auth") } return auth, nil } // response 定义httpbin.org返回的数据结构 type response struct { Args map[string]interface{} `json:"args"` // 请求参数 Headers map[string]string `json:"headers"` // 请求头 } // makeRequest 向httpbin.org发起带认证的GET请求 // ctx: context对象(用于超时控制等) // message: 要发送的消息(会作为查询参数) // token: 认证令牌(会添加到请求头) // 返回: 响应数据或错误 func makeRequest(ctx context.Context, message, token string) (*response, error) { // 创建HTTP请求 req, err := http.NewRequestWithContext(ctx, "GET", "https://httpbin.org/anything", nil) if err != nil { return nil, fmt.Errorf("创建请求失败: %w", err) } // 设置认证头 req.Header.Set("Authorization", token) // 添加查询参数 query := req.URL.Query() query.Add("message", message) req.URL.RawQuery = query.Encode() // 发送请求 resp, err := http.DefaultClient.Do(req) if err != nil { return nil, fmt.Errorf("请求失败: %w", err) } defer resp.Body.Close() // 读取响应体 body, err := io.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("读取响应失败: %w", err) } // 解析JSON响应 var r *response if err := json.Unmarshal(body, &r); err != nil { return nil, fmt.Errorf("解析JSON失败: %w", err) } return r, nil } // handleMakeAuthenticatedRequestTool 认证请求工具的处理函数 // ctx: 包含认证令牌的context // request: MCP工具调用请求 // 返回: 工具调用结果或错误 func handleMakeAuthenticatedRequestTool( ctx context.Context, request mcp.CallToolRequest, ) (*mcp.CallToolResult, error) { // 从请求参数中获取message参数 message, ok := request.Params.Arguments["message"].(string) if !ok { return nil, fmt.Errorf("缺少message参数或参数类型错误") } // 从context中提取认证令牌 token, err := tokenFromContext(ctx) if err != nil { return nil, fmt.Errorf("获取认证令牌失败: %w", err) } // 发起带认证的请求 resp, err := makeRequest(ctx, message, token) if err != nil { return nil, fmt.Errorf("执行请求失败: %w", err) } // 返回格式化后的响应 return mcp.NewToolResultText(fmt.Sprintf("%+v", resp)), nil } // MCPServer 封装MCP服务器的结构体 type MCPServer struct { server *server.MCPServer } // NewMCPServer 创建并配置MCP服务器实例 // 返回: 初始化好的MCPServer指针 func NewMCPServer() *MCPServer { // 创建基础服务器实例 mcpServer := server.NewMCPServer( "example-server", // 服务器名称 "1.0.0", // 服务器版本 server.WithResourceCapabilities(true, true), // 启用资源能力 server.WithPromptCapabilities(true), // 启用提示词能力 server.WithToolCapabilities(true), // 启用工具能力 ) // 注册认证请求工具 mcpServer.AddTool(mcp.NewTool("make_authenticated_request", mcp.WithDescription("执行带认证的HTTP请求"), // 工具描述 mcp.WithString("message", // 字符串参数 mcp.Description("要发送的消息内容"), // 参数描述 mcp.Required(), // 必填参数 ), ), handleMakeAuthenticatedRequestTool) return &MCPServer{ server: mcpServer, } } // ServeSSE 启动SSE模式的服务 // addr: 监听地址(如"localhost:8080") // 返回: SSEServer实例 func (s *MCPServer) ServeSSE(addr string) *server.SSEServer { return server.NewSSEServer(s.server, server.WithBaseURL(fmt.Sprintf("http://%s", addr)), // 基础URL server.WithSSEContextFunc(authFromRequest), // 使用请求头认证 ) } // ServeStdio 启动标准输入输出模式的服务 // 返回: 错误信息(如果有) func (s *MCPServer) ServeStdio() error { return server.ServeStdio(s.server, server.WithStdioContextFunc(authFromEnv), // 使用环境变量认证 ) } func main() { // 解析命令行参数 var transport string flag.StringVar(&transport, "t", "stdio", "传输协议类型(stdio或sse)") flag.StringVar(&transport, "transport", "stdio", "传输协议类型(stdio或sse)") flag.Parse() // 创建服务器实例 s := NewMCPServer() // 根据参数启动对应模式 switch transport { case "stdio": // 标准输入输出模式 if err := s.ServeStdio(); err != nil { log.Fatalf("服务器错误: %v", err) } case "sse": // SSE服务器模式 sseServer := s.ServeSSE("localhost:8080") log.Printf("SSE服务启动,监听 :8080 端口") if err := sseServer.Start(":8080"); err != nil { log.Fatalf("服务器错误: %v", err) } default: log.Fatalf("无效的传输协议: %s。必须是'stdio'或'sse'", transport) } } 这段代码实现了一个基于 MCP(Model Context Protocol) 的服务器,支持通过 SSE(Server-Sent Events) 或 标准输入/输出(stdio) 两种方式与客户端交互。以下是代码的详细解析: 1. 核心功能 认证管理:从 HTTP 请求头或环境变量中提取认证令牌(Authorization),并通过上下文(context.Context)传递。 工具调用:注册一个名为 make_authenticated_request 的工具,用于向外部 API(httpbin.org)发送带认证的请求。 多传输协议支持:支持 SSE 和 stdio 两种通信方式(通过命令行参数切换)。 2. 代码结构解析 (1) 认证管理 authKey 类型:自定义的上下文键,用于存储认证令牌。 withAuthKey:将令牌注入上下文。 authFromRequest:从 HTTP 请求头提取 Authorization 字段并存入上下文。 authFromEnv:从环境变量 API_KEY 中读取令牌并存入上下文。 tokenFromContext:从上下文中提取令牌,供工具函数使用。 (2) 工具实现 makeRequest 函数:向 httpbin.org/anything 发送 GET 请求,附带认证令牌和查询参数 message,返回响应数据。 handleMakeAuthenticatedRequestTool:MCP 工具的核心逻辑: 从请求参数中提取 message。 从上下文中获取认证令牌。 调用 makeRequest 发送请求,返回格式化后的响应。 (3) MCP 服务器配置 NewMCPServer:初始化 MCP 服务器: 设置服务器名称和版本。 启用资源、提示词和工具能力。 注册工具 make_authenticated_request,绑定处理函数 handleMakeAuthenticatedRequestTool。 ServeSSE:启动 SSE 服务器,绑定认证函数 authFromRequest。 ServeStdio:启动 stdio 模式,从环境变量获取认证令牌。 (4) 主函数 通过命令行参数 -t 选择传输协议(stdio 或 sse)。 stdio 模式:直接通过标准输入/输出交互(适合 CLI 或管道调用)。 sse 模式:启动 HTTP 服务(默认端口 8080),通过 SSE 协议通信。 3. 关键逻辑流程 客户端发起请求: 若为 SSE 模式,HTTP 请求头需包含 Authorization。 若为 stdio 模式,需设置环境变量 API_KEY。 认证令牌传递: 服务器通过 authFromRequest 或 authFromEnv 提取令牌,存入上下文。 工具调用: 客户端发送 MCP 格式的请求,指定工具名 make_authenticated_request 和参数 message。 服务器调用 handleMakeAuthenticatedRequestTool,从上下文中获取令牌并发送到 httpbin.org。 返回结果: 工具将 httpbin.org 的响应封装为 MCP 格式返回给客户端。 4. 示例用法 (1) SSE 模式 # 启动服务器 go run main.go -t sse # 客户端请求示例(使用 curl) curl -N -H "Accept: text/event-stream" \ -H "Authorization: Bearer YOUR_TOKEN" \ -X POST \ -d '{"tool":"make_authenticated_request", "params":{"arguments":{"message":"hello"}}}' \ http://localhost:8080/mcp (2) Stdio 模式 # 设置环境变量并启动 export API_KEY="YOUR_TOKEN" go run main.go -t stdio # 通过标准输入发送请求(示例 JSON) echo '{"tool":"make_authenticated_request", "params":{"arguments":{"message":"hello"}}}' | go run main.go 5. 如何运行? 启动SSE服务器: $ go run sse.go -t sse 再启动 mcp inspetor:: $ npx -y @modelcontextprotocol/inspector 运行结果: 20250415000301 连接后可以列出相关工具,此时显示make_authenticated_request,可以进行run tool测试 三、GFast 集成 在实际开发中,很多公司内部的业务有自己的框架,集成了许许多多的独特功能。总不能为了使用 MCP 重写一套 Web 框架,此时就需要使用到 mark3labs/mcp-go 集成到 Web 框架下。因GFast使用的是GoFrame框架,下面以 GoFrame 框架为例: package main import ( "context" "errors" "fmt" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/net/ghttp" "github.com/mark3labs/mcp-go/mcp" "github.com/mark3labs/mcp-go/server" ) func helloHandler2(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { // 从请求参数中获取名字参数,并断言为字符串类型 name, ok := request.Params.Arguments["name"].(string) if !ok { // 如果断言失败,返回错误 return nil, errors.New("name must be a string") } // 返回包含问候语的结果 return mcp.NewToolResultText(fmt.Sprintf("Hello, %s!", name)), nil } func main() { // 创建一个新的 MCPServer 实例(假设这是 SSEServer 所需的) mcpServer := server.NewMCPServer("gf-mcp-server", "1.0.0") // 根据你的实际代码调整 // mcpServer 新加Tool、Resource、Prompt // 添加工具 tool := mcp.NewTool("hello_world", // 工具名称 mcp.WithDescription("Say hello to someone"), // 工具描述 mcp.WithString("name", // 参数名称 mcp.Required(), // 参数是必需的 mcp.Description("Name of the person to greet"), // 参数描述 ), ) // 为工具添加处理器 mcpServer.AddTool(tool, helloHandler2) // 创建一个新的 SSEServer 实例,并传入 MCPServer sseServer := server.NewSSEServer(mcpServer) s := g.Server() // 将 SSEServer 的 SSE 端点和处理函数集成到 GoFrame 路由中 s.BindHandler(sseServer.CompleteSsePath(), func(r *ghttp.Request) { sseServer.ServeHTTP(r.Response.Writer, r.Request) }) // 将 SSEServer 的消息端点和处理函数集成到 GoFrame 路由中 s.BindHandler(sseServer.CompleteMessagePath(), func(r *ghttp.Request) { sseServer.ServeHTTP(r.Response.Writer, r.Request) }) s.SetPort(8000) s.Run() } 运行 启动SSE服务器: $ go run main.go 此时MCPServer 请求响应将被GoFrame接管,并绑定如下路由: 20250416104437 再启动 mcp inspetor:: $ npx -y @modelcontextprotocol/inspector 运行结果: 20250416104240 连接后可以列出相关工具,此时显示hello_world工具,可以进行run tool测试

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

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

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

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)