如何使用Golang开发MCP服务器?MCP-K8S实践
随着大语言模型(LLM)与开发工具的深度融合,Model Control Protocol (MCP)协议正在成为AI与软件工具交互的重要桥梁。MCP允许LLM以结构化的方式调用工具,使AI能够执行具体的操作而不仅仅是生成文本。
本文将详细介绍如何使用Golang和mcp-go[1]这个SDK来开发MCP服务器,并以我的开源项目mcp-k8s[2]为具体案例,讲解如何构建一个与Kubernetes集群交互的MCP服务器。
MCP协议简介
MCP (Model Control Protocol)是一种协议,允许LLM与外部工具进行结构化交互。通过MCP,LLM可以:
1. 获取可用工具及其参数列表
2. 调用这些工具执行操作
3. 获取操作结果并基于结果继续交互
这使得LLM能够"控制"外部系统,执行从查询数据库到操作Kubernetes资源等各种任务。
mcp-go SDK概述
mcp-go[3]是一个用Golang实现的MCP协议SDK,它提供了构建MCP服务器所需的核心组件:
1. 工具注册和管理
2. 协议消息处理
3. 多种传输方式支持(stdio、HTTP SSE等)
使用mcp-go,我们可以快速构建自己的MCP服务器,而无需关心底层协议细节。
开发MCP服务器的基本步骤
使用mcp-go开发MCP服务器通常包括以下步骤:
1. 设计并定义工具(Tools)
2. 实现工具的具体功能
3. 创建服务器并注册工具
4. 配置并启动服务器
下面我们将详细介绍每一步。
1. 设计并定义工具
在MCP中,工具(Tool)是LLM可以调用的功能单元。每个工具需要定义:
• 名称(Name):工具的标识符
• 描述(Description):工具的功能描述
• 参数(Parameters):工具接受的参数及其类型
• 处理函数(Handler):实现工具功能的函数
以mcp-k8s中的get_api_resources
工具为例:
// 定义工具
var getAPIResourcesTool = server.Tool{
Name: "get_api_resources",
Description: "获取集群中所有支持的API资源类型",
Parameters: nil, // 此工具不需要参数
Handler: handleGetAPIResources,
}
// 实现处理函数
func handleGetAPIResources(ctx context.Context, rawParams json.RawMessage) (interface{}, error) {
// 具体实现...
}
2. 实现工具的具体功能
工具的Handler函数实现具体的业务逻辑。在mcp-k8s中,这些函数主要与Kubernetes API交互。
例如,获取API资源列表的实现:
func handleGetAPIResources(ctx context.Context, rawParams json.RawMessage) (interface{}, error) {
// 创建k8s discovery客户端
discoveryClient, err := discovery.NewDiscoveryClientForConfig(kubeConfig)
if err != nil {
returnnil, fmt.Errorf("创建discovery客户端失败: %w", err)
}
// 获取服务器上所有API组
apiGroups, err := discoveryClient.ServerGroups()
if err != nil {
returnnil, fmt.Errorf("获取API组失败: %w", err)
}
// 处理结果并返回
result := processAPIGroups(apiGroups)
return result, nil
}
3. 创建服务器并注册工具
使用mcp-go创建MCP服务器并注册工具:
func main() {
// 创建MCP服务器
s, err := server.NewServer(
server.WithLogger(log.Default()),
)
if err != nil {
log.Fatalf("创建服务器失败: %v", err)
}
// 注册工具
s.RegisterTool(getAPIResourcesTool)
s.RegisterTool(getResourceTool)
s.RegisterTool(listResourcesTool)
// ... 注册更多工具
// 启动服务器
if err := s.Start(); err != nil {
log.Fatalf("启动服务器失败: %v", err)
}
}
4. 配置并启动服务器
mcp-go支持多种传输方式,最常用的是stdio(标准输入/输出)和SSE(Server-Sent Events)。
// stdio模式(默认)
s, err := server.NewServer(
server.WithLogger(log.Default()),
server.WithTransport(server.TransportStdio),
)
// SSE模式
s, err := server.NewServer(
server.WithLogger(log.Default()),
server.WithTransport(server.TransportSSE),
server.WithHost("localhost"),
server.WithPort(8080),
)
mcp-k8s: 一个完整的实践案例
mcp-k8s[4]是一个使用mcp-go开发的、用于与Kubernetes集群交互的MCP服务器。它提供了以下工具:
1.资源类型查询工具
•
get_api_resources
: 获取集群中所有支持的API资源类型
2.资源操作工具
•
get_resource
: 获取特定资源的详细信息•
list_resources
: 列出某种资源类型的所有实例•
create_resource
: 创建新资源(可配置禁用)•
update_resource
: 更新现有资源(可配置禁用)•
delete_resource
: 删除资源(可配置禁用)
以下是mcp-k8s的核心实现:
目录结构
mcp-k8s/ ├── cmd/ │ └── server/ │ └── main.go # 主程序入口 ├── internal/ │ ├── config/ # 配置处理 │ ├── k8s/ # Kubernetes客户端 │ └── tools/ # MCP工具实现 ├── go.mod └── go.sum
主程序入口
// cmd/server/main.go func main() { // 解析命令行参数 kubeconfig := flag.String("kubeconfig", "", "Kubernetes配置文件路径") enableCreate := flag.Bool("enable-create", false, "启用资源创建操作") enableUpdate := flag.Bool("enable-update", false, "启用资源更新操作") enableDelete := flag.Bool("enable-delete", false, "启用资源删除操作") transport := flag.String("transport", "stdio", "传输方式: stdio或sse") host := flag.String("host", "localhost", "SSE模式的主机名") port := flag.Int("port", 8080, "SSE模式的端口") flag.Parse() // 初始化Kubernetes客户端 if err := k8s.InitClient(*kubeconfig); err != nil { log.Fatalf("初始化K8s客户端失败: %v", err) } // 创建MCP服务器 serverOpts := []server.Option{ server.WithLogger(log.Default()), } // 配置传输方式 if *transport == "sse" { serverOpts = append(serverOpts, server.WithTransport(server.TransportSSE), server.WithHost(*host), server.WithPort(*port), ) } else { serverOpts = append(serverOpts, server.WithTransport(server.TransportStdio)) } s, err := server.NewServer(serverOpts...) if err != nil { log.Fatalf("创建服务器失败: %v", err) } // 注册工具 tools.RegisterTools(s, &tools.Options{ EnableCreate: *enableCreate, EnableUpdate: *enableUpdate, EnableDelete: *enableDelete, }) // 启动服务器 if err := s.Start(); err != nil { log.Fatalf("启动服务器失败: %v", err) } }
工具实现示例
以
get_resource
工具为例:// internal/tools/get_resource.go var GetResourceTool = server.Tool{ Name: "get_resource", Description: "获取特定资源的详细信息", Parameters: []server.Parameter{ { Name: "apiVersion", Description: "资源的API版本", Type: "string", Required: true, }, { Name: "kind", Description: "资源类型", Type: "string", Required: true, }, { Name: "name", Description: "资源名称", Type: "string", Required: true, }, { Name: "namespace", Description: "资源所在的命名空间(如适用)", Type: "string", Required: false, }, }, Handler: handleGetResource, } type GetResourceParams struct { APIVersion string`json:"apiVersion"` Kind string`json:"kind"` Name string`json:"name"` Namespace string`json:"namespace"` } func handleGetResource(ctx context.Context, rawParams json.RawMessage) (interface{}, error) { var params GetResourceParams if err := json.Unmarshal(rawParams, ¶ms); err != nil { returnnil, fmt.Errorf("解析参数失败: %w", err) } // 获取动态客户端 dynamicClient, err := k8s.GetDynamicClient() if err != nil { returnnil, err } // 获取资源GVR (Group Version Resource) gvr, namespaced, err := k8s.GetGVR(params.APIVersion, params.Kind) if err != nil { returnnil, err } // 确定是命名空间级别还是集群级别的资源 var object *unstructured.Unstructured if namespaced { // 如果是命名空间级别资源但未提供命名空间,使用默认命名空间 namespace := params.Namespace if namespace == "" { namespace = "default" } object, err = dynamicClient.Resource(gvr).Namespace(namespace).Get(ctx, params.Name, metav1.GetOptions{}) } else { object, err = dynamicClient.Resource(gvr).Get(ctx, params.Name, metav1.GetOptions{}) } if err != nil { returnnil, fmt.Errorf("获取资源失败: %w", err) } return object.Object, nil }
注册所有工具
// internal/tools/tools.go type Options struct { EnableCreate bool EnableUpdate bool EnableDelete bool } func RegisterTools(s *server.Server, opts *Options) { // 注册查询工具(始终启用) s.RegisterTool(GetAPIResourcesTool) s.RegisterTool(GetResourceTool) s.RegisterTool(ListResourcesTool) // 根据配置注册写操作工具 if opts.EnableCreate { s.RegisterTool(CreateResourceTool) } if opts.EnableUpdate { s.RegisterTool(UpdateResourceTool) } if opts.EnableDelete { s.RegisterTool(DeleteResourceTool) } }
使用方法
mcp-k8s支持两种通信模式:
1. Stdio模式(默认)
在stdio模式下,mcp-k8s通过标准输入/输出流与客户端通信。
// MCP客户端配置 { "mcpServers":{ "mcp-k8s":{ "command":"/path/to/mcp-k8s", "args":[ "-kubeconfig","/path/to/kubeconfig", "-enable-create", "-enable-delete", "-enable-update" ] } } }
2. SSE模式
在SSE模式下,mcp-k8s暴露HTTP端点供MCP客户端连接。
# 运行SSE模式服务 ./bin/mcp-k8s -kubeconfig=/path/to/kubeconfig -transport=sse -port=8080 -host=localhost -enable-create -enable-delete -enable-update
// MCP客户端配置 { "mcpServers": { "mcp-k8s": { "url": "http://localhost:8080/sse", "args": [] } } }
MCP服务开发的最佳实践
基于开发mcp-k8s的经验,我总结了以下开发MCP服务的最佳实践:
1.工具命名与分类:使用清晰、一致的命名方式,按功能分类工具
2.参数设计:参数名称要直观,提供清晰的描述,明确标记必选参数
3.安全控制:对写操作提供独立的开关控制,避免不必要的权限风险
4.错误处理:返回详细的错误信息,帮助LLM理解失败原因
5.状态管理:MCP服务应保持无状态,所有必要信息通过参数传递
6.文档完善:详细记录每个工具的功能、参数和使用示例
结论
使用Golang和mcp-go SDK开发MCP服务器是一个相对简单的过程。通过定义和实现工具,我们可以让LLM具备与各种系统交互的能力,从而大大扩展其应用场景。
mcp-k8s项目展示了如何构建一个功能完整的MCP服务器,让LLM能够查询、创建、更新和删除Kubernetes资源,为集群管理提供了新的交互方式。
希望本文能够帮助你理解MCP协议和使用Golang开发MCP服务器的基本方法。更多详情,欢迎访问mcp-k8s[5]项目,或查看mark3labs/mcp-go[6]的文档。
AI&&MCP交流群
我建了一个AI技术交流群,目的是为了方便大家交流AI相关的知识和共享资源,目前AI变化真的是太快了
如果二维码失效大家可以关注公众号,发送关键字”加群“,拉你进去;参考资料
1.MCP协议规范[7]
2.mcp-go SDK[8]
3.mcp-k8s项目[9]
4.Kubernetes client-go文档[10]
引用链接
[1]
mcp-go:https://github.com/mark3labs/mcp-go[2]
mcp-k8s:https://github.com/silenceper/mcp-k8s[3]
mcp-go:https://github.com/mark3labs/mcp-go[4]
mcp-k8s:https://github.com/silenceper/mcp-k8s[5]
mcp-k8s:https://github.com/silenceper/mcp-k8s[6]
mark3labs/mcp-go:https://github.com/mark3labs/mcp-go[7]
MCP协议规范:https://github.com/llm-tools/model-control-protocol[8]
mcp-go SDK:https://github.com/mark3labs/mcp-go[9]
mcp-k8s项目:https://github.com/silenceper/mcp-k8s[10]
Kubernetes client-go文档:https://github.com/kubernetes/client-go
本文地址:https://www.vps345.com/1318.html