mcp-go Go搭建MCP服務(wù)的實(shí)現(xiàn)示例
介紹
目前Go 生態(tài)圈有兩個(gè)知名的開發(fā) MCP 的庫,一個(gè)是mark3labs/mcp-go,另一個(gè)是metoro-io/mcp-golang。
在介紹常用庫之前,先來簡(jiǎn)單介紹一下mcp協(xié)議:
MCP全稱Model Context Protocol 模型上下文協(xié)議,MCP 是一個(gè)開放協(xié)議,它為應(yīng)用程序向 LLM 提供上下文的方式進(jìn)行了標(biāo)準(zhǔn)化。你可以將 MCP 想象成 AI 應(yīng)用程序的 USB-C 接口。就像 USB-C 為設(shè)備連接各種外設(shè)和配件提供了標(biāo)準(zhǔn)化的方式一樣,MCP 為 AI 模型連接各種數(shù)據(jù)源和工具提供了標(biāo)準(zhǔn)化的接口。
相比之前各大AI廠商各自為戰(zhàn),接口以及協(xié)議之間不通用,導(dǎo)致開發(fā)者針對(duì)同一個(gè)功能為了適配多個(gè)AI模型,需要開發(fā)多套。現(xiàn)在有了MCP,各大模型廠商和開發(fā)者只需遵循MCP協(xié)議,即可實(shí)現(xiàn)一次編寫,多模型適用。
MCP與大模型調(diào)用之間原理圖:
MCP架構(gòu)主要包含一下幾個(gè)模塊:
- MCP Hosts: 如 Claude Desktop、IDE 、DeepChat或 AI 工具,希望通過 MCP 訪問數(shù)據(jù)的程序
- MCP Clients: 維護(hù)與服務(wù)器一對(duì)一連接的協(xié)議客戶端
- MCP Servers: 輕量級(jí)程序,通過標(biāo)準(zhǔn)的 Model Context Protocol 提供特定能力
- 本地?cái)?shù)據(jù)源: MCP 服務(wù)器可安全訪問的計(jì)算機(jī)文件、數(shù)據(jù)庫和服務(wù)
- 遠(yuǎn)程服務(wù): MCP 服務(wù)器可連接的互聯(lián)網(wǎng)上的外部系統(tǒng)(如通過 APIs)
安裝
go get "github.com/mark3labs/mcp-go"
API介紹
1. server.NewMCPServer:新建一個(gè)mcp server
目前MCP 支持 SSE 和Stdio兩種類型。
stdio:standard input output,標(biāo)準(zhǔn)輸入輸出,適用于mcp client和mcp server部署在同一個(gè)機(jī)器上,不涉及跨機(jī)器。
sse:Server-Sent Events,服務(wù)器發(fā)送事件,是一個(gè)基于HTTP的協(xié)議。適用于mcp client與mcp server不在同一個(gè)機(jī)器中的場(chǎng)景,涉及網(wǎng)絡(luò)傳輸。
這里我們以新建一個(gè)基于Stdio協(xié)議的mcp server為例:
//新建mcp 服務(wù) mcpServer := server.NewMCPServer("ziyi Mcp Server", "1.0.0") //對(duì)外提供 stdio mcp server服務(wù) if err := server.ServeStdio(mcpServer); err != nil { panic(err) }
2. mcp.NewResource:對(duì)外提供資源
資源是你向 LLMs 暴露數(shù)據(jù)的方式。它們可以是任何東西:文件、API 響應(yīng)、數(shù)據(jù)庫查詢、系統(tǒng)信息等。資源可以是:
- 靜態(tài)資源(固定 URI)
- 動(dòng)態(tài)資源(使用 URI 模板)
//新增一個(gè)mcp server對(duì)外暴露的靜態(tài)資源(固定URI),比如:對(duì)外暴露項(xiàng)目的操作手冊(cè)以及部署方式等,就可以通過靜態(tài)資源README文件的方式來告訴大模型 resource := mcp.NewResource( "docs://readme", "項(xiàng)目的README文件", mcp.WithResourceDescription("這是一個(gè)項(xiàng)目的README文件"), mcp.WithMIMEType("text/markdown"), ) // 添加對(duì)靜態(tài)資源的處理器 mcpServer.AddResource(resource, func(ctx context.Context, request mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) { content, err := os.ReadFile("README.md") if err != nil { return nil, err } return []mcp.ResourceContents{ mcp.TextResourceContents{ URI: "docs://readme", MIMEType: "text/markdown", Text: string(content), }, }, nil })
3. mcp.NewTool:對(duì)外提供工具
工具讓 LLM 通過你的服務(wù)器執(zhí)行操作。與資源不同,工具預(yù)期會(huì)進(jìn)行計(jì)算并產(chǎn)生副作用。它們類似于 REST API 中的 POST 端點(diǎn)。 下面是一個(gè)算術(shù)運(yùn)算的工具示例:
工具可以用于任何種類的計(jì)算:
Database queries 數(shù)據(jù)庫查詢
File operations 文件操作
External API calls 外部 API 調(diào)用
Calculations 計(jì)算
System operations 系統(tǒng)操作
每個(gè)工具應(yīng)該:
有清晰的描述
驗(yàn)證輸入
優(yōu)雅處理錯(cuò)誤
返回結(jié)構(gòu)化的響應(yīng)
使用適當(dāng)?shù)慕Y(jié)果類型
//給該mcp server添加計(jì)算能力(Tools) // 1. 描述該工具,以及調(diào)用該工具調(diào)用的參數(shù)機(jī)器含義 calculatorTool := mcp.NewTool("calculate", mcp.WithDescription("進(jìn)行基礎(chǔ)的數(shù)學(xué)運(yùn)算"), mcp.WithString("operation", mcp.Required(), mcp.Description("The arithmetic operation to perform"), mcp.Enum("add", "subtract", "multiply", "divide"), ), mcp.WithNumber("x", mcp.Required(), mcp.Description("First number"), ), mcp.WithNumber("y", mcp.Required(), mcp.Description("Second number"), ), ) // 2. 實(shí)現(xiàn)工具的具體處理邏輯,類比大模型function_calling中的func部分 mcpServer.AddTool(calculatorTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { op := request.Params.Arguments["operation"].(string) x := request.Params.Arguments["x"].(float64) y := request.Params.Arguments["y"].(float64) var result float64 switch op { case "add": result = x + y case "subtract": result = x - y case "multiply": result = x * y case "divide": if y == 0 { return nil, errors.New("Division by zero is not allowed") } result = x / y } return mcp.FormatNumberResult(result), nil })
4. s.AddPrompt:添加提示詞
下面是一個(gè)簡(jiǎn)單的提示詞示例,它需要一個(gè)名稱參數(shù),然后返回一個(gè)問候提示詞:
// 給mcpServer添加提示詞模版 mcpServer.AddPrompt(mcp.NewPrompt("打招呼", mcp.WithPromptDescription("A friendly greeting prompt"), mcp.WithArgument("name", mcp.ArgumentDescription("Name of the person to greet"), ), ), func(ctx context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) { name := request.Params.Arguments["name"] if name == "" { name = "friend" } return mcp.NewGetPromptResult( "A friendly greeting", []mcp.PromptMessage{ mcp.NewPromptMessage( mcp.RoleAssistant, mcp.NewTextContent(fmt.Sprintf("Hello, %s! How can I help you today?", name)), ), }, ), nil })
實(shí)戰(zhàn)使用
1. 編寫mcp server
這里我們演示一個(gè)查詢IP的mcp server。大模型傳入ip,mcp server返回地址信息。
ip mcp server全部代碼:
package main import ( "context" "errors" "fmt" "github.com/mark3labs/mcp-go/mcp" "github.com/mark3labs/mcp-go/server" "io" "net" "net/http" ) func main() { // Create MCP server s := server.NewMCPServer( "ip-mcp", "1.0.0", ) // Add tool tool := mcp.NewTool("ip_query", mcp.WithDescription("query geo location of an IP address"), mcp.WithString("ip", mcp.Required(), mcp.Description("IP address to query"), ), ) // Add tool handler s.AddTool(tool, ipQueryHandler) // Start the stdio server if err := server.ServeStdio(s); err != nil { fmt.Printf("Server error: %v\n", err) } } func ipQueryHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { ip, ok := request.Params.Arguments["ip"].(string) if !ok { return nil, errors.New("ip must be a string") } parsedIP := net.ParseIP(ip) if parsedIP == nil { return nil, errors.New("invalid IP address") } resp, err := http.Get("https://ip.rpcx.io/api/ip?ip=" + ip) if err != nil { return nil, fmt.Errorf("Error fetching IP information: %v", err) } defer resp.Body.Close() data, err := io.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("Error reading response body: %v", err) } fmt.Printf("call ip: %s Response body: %s\n", ip, string(data)) return mcp.NewToolResultText(string(data)), nil }
然后我們編譯代碼:
# 執(zhí)行命令編譯mcp server go build -o mcp-ip main.go
2. 桌面版調(diào)用mcp server插件
這里使用桌面版的deepchat來演示大模型調(diào)用mcp server。
deepchat下載地址:https://deepchat.thinkinai.xyz/#/download
- 下載后配置本地大模型(通過ollama方式搭建等)或者直接配置遠(yuǎn)程模型地址
遠(yuǎn)程模型這里我使用openrouter的免費(fèi)deepseek-r1版本:https://openrouter.ai/deepseek/deepseek-r1:free
選擇模型,配置URL以及Token后,點(diǎn)擊校驗(yàn)
點(diǎn)擊下一步,然后就可以與大模型對(duì)話了
tips:
可修改頁面文字為中文
可修改對(duì)話模型
下面我們配置mcp server
跳過json方式配置,轉(zhuǎn)為手動(dòng)配置
填寫mcp server配置參數(shù),然后點(diǎn)擊提交
啟用mcp server
- 輸入問題,查看效果
如:幫我查詢39.156.66.10 IP信息
參考文章:
https://mcp-docs.cn/introduction
https://mp.weixin.qq.com/s/UgdXztu3SKYMs56DqiX3Sg
https://www.junki.cn/archives/Zqgi7fzK
到此這篇關(guān)于mcp-go Go搭建MCP服務(wù)的實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)mcp-go Go搭建MCP服務(wù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go語言結(jié)合grpc和protobuf實(shí)現(xiàn)去中心化的聊天室
這篇文章主要為大家詳細(xì)介紹了Go語言如何結(jié)合grpc和protobuf實(shí)現(xiàn)去中心化的聊天室,文中的示例代碼講解詳細(xì),有需要的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-03-03Golang校驗(yàn)字符串是否JSON格式的方法總結(jié)
這篇文章主要為大家詳細(xì)介紹了Golang中校驗(yàn)字符串是否JSON格式的方法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解一下2023-04-04MacOS下本地golang環(huán)境搭建詳細(xì)教程
這篇文章主要介紹了MacOS下本地golang環(huán)境搭建詳細(xì)教程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09Goland IDEA項(xiàng)目多開設(shè)置方式
這篇文章主要介紹了Goland IDEA項(xiàng)目多開設(shè)置方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12