ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

如何使用Golang开发MCP服务器?MCP-K8S实践:TaoToken统一Key接入与配置骨架

如何使用Golang开发MCP服务器?MCP-K8S实践:TaoToken统一Key接入与配置骨架 1. 为什么要在 Golang 里手搓一个 MCP 服务器MCP 服务器说白了就是给大模型装的一双手模型本身只会输出文字但通过 MCP 协议它可以按结构化格式调用你注册的工具去查 K8s 资源、读日志、改配置。Golang 写 MCP 服务器的好处很直接——编译出来是单个二进制扔到跳板机或容器里就能跑没有 Python 那套虚拟环境依赖特别适合 MCP-K8S 这种要贴着集群跑的运维场景。我这次的目标很明确用 Golang 从零搭一个能连 Kubernetes 的 MCP 服务器工具集覆盖get_api_resources、get_resource、list_resources这几个查询动作写操作先留开关默认关闭。然后把它接到 AI 工具链里让模型能通过统一入口调用。这里的关键卡点其实不在 Go 代码而在“模型侧怎么稳定拿到 Key、怎么把 MCP 服务器注册进客户端”。所以我会用 TaoToken 做统一 Key 和 API 通道把模型调用和 MCP 工具调用串成一条链路最后交付可复制的config.toml和settings.json骨架。适合谁看会一点 Go、手上有个测试集群、想让 AI 帮你查 Pod 和 Deployment 的同学。全程不需要你懂协议底层照着配置改路径就能跑。2. TaoToken 前置准备统一 Key 与通道在写代码之前先把模型侧的入口准备好。TaoToken 在这里扮演的是统一 Key 和 API 通道的角色——你不用在 Cline、CC Switch、Coding Plan 里各配一套 Key而是拿一个 Key 走同一个 API 地址MCP 服务器和模型对话都从这里过。第一步打开官网注册并进入控制台https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content第二步在控制台里创建 API Key。路径是 console 页面下的 api-keyshttps://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_campaignrewriteutm_content创建完把 Key 复制出来形如sk-xxxx先存到环境变量里别硬编码进代码export TAOTOKEN_API_KEYsk-你的key第三步确认 API 基地址。所有请求走这个地址注意它不带任何查询参数https://taotoken.net/api如果你后面要用 Claude Code 这类工具Anthropic 兼容入口在https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_campaignrewriteutm_content注意Key 只创建一次就够多个客户端共用同一个 Key。如果怀疑泄露在 api-keys 页面直接吊销重建不用改代码。3. Golang MCP 服务器配置骨架3.1 项目结构与依赖先建目录用mcp-go这个 SDK它把协议消息处理、工具注册、stdio/SSE 传输都封装好了mkdir mcp-k8s cd mcp-k8s go mod init github.com/yourname/mcp-k8s go get github.com/mark3labs/mcp-go go get k8s.io/client-gov0.29.0 go get k8s.io/apimachineryv0.29.0目录按职责拆开后面加工具不会乱mcp-k8s/ ├── cmd/server/main.go # 入口解析参数、启动 ├── internal/config/ # 读取 config.toml ├── internal/k8s/ # K8s 客户端初始化 └── internal/tools/ # 每个工具一个文件3.2 config.toml 配置骨架把模型通道和 K8s 连接信息都收进一个配置文件代码里只读不写死。下面这份可以直接复制改# config.toml [server] transport stdio # stdio 或 sse host localhost port 8080 [ai] # TaoToken 统一通道 base_url https://taotoken.net/api api_key_env TAOTOKEN_API_KEY # 从环境变量读不落盘 model claude-3-5-sonnet [k8s] kubeconfig /home/you/.kube/config context [tools] enable_create false enable_update false enable_delete false读取用BurntSushi/toml几行就够package config import github.com/BurntSushi/toml type Config struct { Server struct { Transport string toml:transport Host string toml:host Port int toml:port } toml:server AI struct { BaseURL string toml:base_url APIKeyEnv string toml:api_key_env Model string toml:model } toml:ai K8s struct { Kubeconfig string toml:kubeconfig Context string toml:context } toml:k8s Tools struct { EnableCreate bool toml:enable_create EnableUpdate bool toml:enable_update EnableDelete bool toml:enable_delete } toml:tools } func Load(path string) (*Config, error) { var c Config if _, err : toml.DecodeFile(path, c); err ! nil { return nil, err } return c, nil }3.3 注册 K8s 查询工具工具定义三要素名字、描述、参数。描述要写清楚模型靠它判断什么时候调。以get_resource为例package tools import ( context encoding/json fmt github.com/mark3labs/mcp-go/server k8s.io/apimachinery/pkg/apis/meta/v1/unstructured metav1 k8s.io/apimachinery/pkg/apis/meta/v1 ) var GetResourceTool server.Tool{ Name: get_resource, Description: 获取指定 Kubernetes 资源的详细信息需要 apiVersion、kind、name, Parameters: []server.Parameter{ {Name: apiVersion, Description: 资源 API 版本如 v1, Type: string, Required: true}, {Name: kind, Description: 资源类型如 Pod, 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, raw json.RawMessage) (interface{}, error) { var p getResourceParams if err : json.Unmarshal(raw, p); err ! nil { return nil, fmt.Errorf(参数解析失败: %w, err) } dyn, err : k8s.DynamicClient() if err ! nil { return nil, err } gvr, namespaced, err : k8s.ResolveGVR(p.APIVersion, p.Kind) if err ! nil { return nil, err } var obj *unstructured.Unstructured if namespaced { ns : p.Namespace if ns { ns default } obj, err dyn.Resource(gvr).Namespace(ns).Get(ctx, p.Name, metav1.GetOptions{}) } else { obj, err dyn.Resource(gvr).Get(ctx, p.Name, metav1.GetOptions{}) } if err ! nil { return nil, fmt.Errorf(获取资源失败: %w, err) } return obj.Object, nil }ResolveGVR用 discovery 客户端把apiVersionkind映射成 GVR这是动态客户端调用的必要一步别跳过。3.4 启动入口与传输选择入口负责把配置、K8s 客户端、工具注册串起来func main() { cfgPath : flag.String(config, config.toml, 配置文件路径) flag.Parse() cfg, err : config.Load(*cfgPath) if err ! nil { log.Fatalf(加载配置失败: %v, err) } if err : k8s.Init(cfg.K8s.Kubeconfig, cfg.K8s.Context); err ! nil { log.Fatalf(初始化 K8s 客户端失败: %v, err) } opts : []server.ServerOption{server.WithLogger(log.Default())} if cfg.Server.Transport sse { opts append(opts, server.WithTransport(server.TransportSSE), server.WithHost(cfg.Server.Host), server.WithPort(cfg.Server.Port), ) } else { opts append(opts, server.WithTransport(server.TransportStdio)) } s, err : server.NewServer(opts...) if err ! nil { log.Fatalf(创建服务器失败: %v, err) } tools.Register(s, tools.Options{ EnableCreate: cfg.Tools.EnableCreate, EnableUpdate: cfg.Tools.EnableUpdate, EnableDelete: cfg.Tools.EnableDelete, }) if err : s.Start(); err ! nil { log.Fatalf(启动失败: %v, err) } }编译go build -o bin/mcp-k8s ./cmd/server4. 客户端接入与连通性验证4.1 settings.json 配置骨架Cline 或 CC Switch 里注册 MCP 服务器stdio 模式直接指向二进制{ mcpServers: { mcp-k8s: { command: /home/you/mcp-k8s/bin/mcp-k8s, args: [-config, /home/you/mcp-k8s/config.toml], env: { TAOTOKEN_API_KEY: sk-你的key } } } }如果走 SSE 模式先启动服务./bin/mcp-k8s -config config.toml # config.toml 里 transport sse客户端改成 URL 形式{ mcpServers: { mcp-k8s: { url: http://localhost:8080/sse } } }4.2 验证请求与成功结果先单独验证 MCP 服务器能不能连上集群。用 stdio 模式手动喂一条初始化请求echo {jsonrpc:2.0,id:1,method:tools/list,params:{}} | ./bin/mcp-k8s -config config.toml正常会返回工具列表包含get_api_resources、get_resource、list_resources。如果返回空数组说明工具注册那步没生效回去检查Register函数。再验证模型通道。用 curl 打 TaoToken 的 API确认 Key 有效curl -s https://taotoken.net/api/v1/messages \ -H x-api-key: $TAOTOKEN_API_KEY \ -H anthropic-version: 2023-06-01 \ -H content-type: application/json \ -d { model: claude-3-5-sonnet, max_tokens: 64, messages: [{role:user,content:ping}] }返回里带content字段就说明通道通了。最后在客户端里发一句“列出 default 命名空间的 Pod”模型会调用list_resources你能看到工具调用记录和返回的 Pod 列表这条本地到集群的链路就算跑通了。5. 本篇常见错排查报错一exec: mcp-k8s: executable file not foundsettings.json 里的command必须写绝对路径。相对路径在客户端的工作目录下解析基本找不到。用which或realpath确认。报错二unable to load kubeconfigconfig.toml里的kubeconfig路径写的是~/.kube/config但 Go 不展开~。改成/home/you/.kube/config这种绝对路径。报错三工具调用返回the server does not support the resourceResolveGVR没查到对应资源。检查apiVersion和kind大小写Pod 是v1PodDeployment 是apps/v1Deployment。可以先调get_api_resources看集群支持哪些。报错四SSE 模式客户端连不上host配成localhost时如果客户端在容器里要改成0.0.0.0或容器可达的地址。端口被占用就换port。报错五模型通道返回 401TAOTOKEN_API_KEY没传进 MCP 服务器的进程环境。stdio 模式下客户端启动子进程时env字段要显式带上别指望继承 shell 变量。6. 把链路固定下来跑通之后建议做两件事。一是把config.toml里的写操作开关保持false等查询类工具用稳了再逐个打开避免模型误删资源。二是把 MCP 服务器和模型通道分开验证先用tools/list确认工具注册没问题再用 curl 确认 Key 有效最后才在客户端里联调。这样出问题时能快速定位是 Go 侧还是通道侧。长期在编码和 Agent 场景里用的话可以走 Coding Plan 把模型调用额度固定下来https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_campaignrewriteutm_content需要看完整接入文档和参数说明的从这里进https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_campaignrewriteutm_content模型对话调试入口https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content我自己的习惯是先把get_api_resources和list_resources这两个只读工具调顺确认模型能正确拼参数再考虑加写操作。MCP-K8S 这类贴着集群跑的服务权限收得越紧越省心。
返回列表