云计算百科
云计算领域专业知识百科平台

如何使用Golang开发MCP服务器?MCP-K8S实践

我建了一个AI技术交流群,发送关键字”加群“,拉你入群

随着大语言模型(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, &params); 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

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 如何使用Golang开发MCP服务器?MCP-K8S实践
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!