GFast开发MCP服务器之mark3labs/mcp-go库接入(一)
这将是一个系列文章,我们将从简到繁开发一套基于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 -
点击
connect:连接后可以列出相关工具,此时显示
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)

