使用 Go 構建 MCP Server
一、MCP 介紹
1. 基本介紹
MCP(Model Context Protocol,模型上下文協議)是由 Anthropic 公司(Claude 大模型的創造者)於 2024 年 11 月推出的一種開放標準協議,旨在統一大型語言模型(LLM)與外部數據源和工具之間的通信方式。MCP 的核心目標是解決當前 AI 應用開發中的數據孤島和碎片化集成問題。
2. 協議特點
MCP 可以被理解爲 AI 大模型的 "萬能接口",類似於 USB-C 接口在硬件領域的作用,它提供了一種標準化的方法,使 AI 模型能夠與不同的數據源和工具進行無縫交互。通過 MCP,開發者可以更輕鬆地構建複雜的 AI 應用,而無需爲每個工具或數據源編寫專門的集成代碼。
-
MCP 是一個標準協議,如同電子設備的 Type C 協議 (可以充電也可以傳輸數據),使 AI 模型能夠與不同的 API 和數據源無縫交互。
-
MCP 旨在替換碎片化的 Agent 代碼集成,從而使 AI 系統更可靠,更有效。通過建立通用標準,服務商可以基於協議來推出它們自己服務的 AI 能力,從而支持開發者更快的構建更強大的 AI 應用。開發者也不需要重複造輪子,通過開源項目可以建立強大的 AI Agent 生態。
-
MCP 可以在不同的應用 / 服務之間保持上下文,從而增強整體自主執行任務的能力。
可以理解爲 MCP 是將不同任務進行分層處理,每一層都提供特定的能力、描述和限制。而 MCP Client 端根據不同的任務判斷,選擇是否需要調用某個能力,然後通過每層的輸入和輸出,構建一個可以處理複雜、多步對話和統一上下文的 Agent。
3. AI Agent 和 MCP 間的關係
-
AI Agent 是一個智能系統,它可以自主運行以實現特定目標。傳統的 AI 聊天僅提供建議或者需要手動執行任務,AI Agent 則可以分析具體情況,做出決策,並自行採取行動。
-
AI Agent 可以利用 MCP 提供的功能描述來理解更多的上下文,並在各種平臺 / 服務自動執行任務。
4. MCP 如何工作
MCP 採用客戶端 - 服務器架構,MCP 架構主要包含以下核心組件:
MCP 的基本工作流程如下:
-
用戶向 AI 模型(MCP 客戶端)發送請求
-
AI 模型分析請求,確定需要調用的外部工具或數據
-
AI 模型通過 MCP 協議向相應的 MCP 服務器發送請求
-
MCP 服務器處理請求並返回結果
-
AI 模型整合結果,生成最終回覆給用戶
二、cline 配置本地模型
我做測試使用的客戶端是 vscode+cline,cline 在 vscode 的插件市場中直接安裝即可。cline 對 MCP 的支持也是非常好的。因爲要使用到大模型,所以這裏還要給 cline 配置一個大模型。
外網開放的免費模型一般都不太好用,所以我一般都是配置本地模型來測試,這裏也介紹一下 cline 如何配置本地模型。
我這裏配置了 ollama 啓動的 qwen2.5:14b 模型。如下圖配置即可,這個配置過程比較簡單。
三、MCP 服務開發
1. 典型應用場景
-
實時數據分析:LLM 通過 MCP 直接查詢數據庫生成動態報告(如銷售數據可視化)。
-
跨平臺自動化:結合本地文件讀寫和 API 調用,實現 “讀取文檔 → 生成會議摘要 → 發送郵件” 全流程自動化。
-
隱私敏感任務:醫療數據存儲在本地 Server,模型處理時不外傳,符合 GDPR 合規要求。
-
工具增強生成:例如代碼編輯器中集成 MCP Server,根據用戶需求調用圖像生成工具自動插入圖片。
2. 案例開發
這部分代碼參考了這篇文章:https://mp.weixin.qq.com/s/JmPxMBRZa8UhsIOQVgupLw ,這篇文章主要是一個按照時區獲取時間的工具,我在測試 ok 之後擴展了一個天氣獲取的工具。
天氣接口使用的是騰訊雲的接口,大家可以直接使用免費額度,代碼也是直接從騰訊雲 api 的介紹中拷貝過來的,這裏做了簡單的封裝調用。
下面的代碼直接拷貝過去就可以使用,不過天氣 api 的 key 要替換成你自己的。
package main
import (
"context"
"crypto/hmac"
"crypto/sha1"
"encoding/base64"
"fmt"
"io"
"io/ioutil"
"net/http"
gourl "net/url"
"strings"
"time"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
func main() {
// GetWeaher("北京")
// Create MCP server
s := server.NewMCPServer("MCPDemo", "1.0.0")
// Add Get time tool
timetool := mcp.NewTool("current_time",
mcp.WithDescription("Get current time with timezone, Asia/Shanghai is default"),
mcp.WithString("timezone", mcp.Required(), mcp.Description("current time timezone")))
// Add tool handler
s.AddTool(timetool, currentTimeHandler)
// add weather tool
weathertool := mcp.NewTool("current_weather",
mcp.WithDescription("Get current weather with city name, 北京 is default, 需要輸入中文"),
mcp.WithString("city", mcp.Required(), mcp.Description("city name")))
// Add tool handler
s.AddTool(weathertool, weatherHandler)
// Start the stdio server
if err := server.ServeStdio(s); err != nil {
fmt.Printf("Server error: %v\n", err)
}
}
func currentTimeHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
timezone, ok := request.Params.Arguments["timezone"].(string)
if !ok {
return mcp.NewToolResultError("timezone must be a string"), nil
}
loc, err := time.LoadLocation(timezone)
if err != nil {
return mcp.NewToolResultError(fmt.Sprintf("parse timezone with error: %v", err)), nil
}
return mcp.NewToolResultText(fmt.Sprintf(`current time is %s`, time.Now().In(loc))), nil
}
func weatherHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
city, ok := request.Params.Arguments["city"].(string)
if !ok {
return mcp.NewToolResultError("city must be a string"), nil
}
body, err := GetWeaher(city)
if err != nil {
return mcp.NewToolResultError(fmt.Sprintf("read response body with error: %v", err)), nil
}
return mcp.NewToolResultText(string(body)), nil
}
func calcAuthorization(source string, secretId string, secretKey string) (auth string, datetime string, err error) {
timeLocation, _ := time.LoadLocation("Etc/GMT")
datetime = time.Now().In(timeLocation).Format("Mon, 02 Jan 2006 15:04:05 GMT")
signStr := fmt.Sprintf("x-date: %s\nx-source: %s", datetime, source)
// hmac-sha1
mac := hmac.New(sha1.New, []byte(secretKey))
mac.Write([]byte(signStr))
sign := base64.StdEncoding.EncodeToString(mac.Sum(nil))
auth = fmt.Sprintf("hmac id=\"%s\", algorithm=\"hmac-sha1\", headers=\"x-date x-source\", signature=\"%s\"",
secretId, sign)
return auth, datetime, nil
}
func urlencode(params map[string]string) string {
var p = gourl.Values{}
for k, v := range params {
p.Add(k, v)
}
return p.Encode()
}
func GetWeaher(city string) (ret string, err error) {
// 雲市場分配的密鑰Id
secretId := "xxx"
// 雲市場分配的密鑰Key
secretKey := "xxx"
source := "usagePlan-xxx"
// 簽名
auth, datetime, _ := calcAuthorization(source, secretId, secretKey)
// 請求方法
method := "GET"
// 請求頭
headers := map[string]string{"X-Source": source, "X-Date": datetime, "Authorization": auth}
// 查詢參數
queryParams := make(map[string]string)
queryParams["areaCn"] = city
queryParams["areaCode"] = ""
queryParams["ip"] = ""
queryParams["lat"] = ""
queryParams["lng"] = ""
queryParams["need1hour"] = ""
queryParams["need3hour"] = ""
queryParams["needIndex"] = ""
queryParams["needObserve"] = ""
queryParams["needalarm"] = ""
// body參數
bodyParams := make(map[string]string)
// url參數拼接
url := "https://service-6drgk6su-1258850945.gz.apigw.tencentcs.com/release/lundear/weather1d"
if len(queryParams) > 0 {
url = fmt.Sprintf("%s?%s", url, urlencode(queryParams))
}
bodyMethods := map[string]bool{"POST": true, "PUT": true, "PATCH": true}
var body io.Reader = nil
if bodyMethods[method] {
body = strings.NewReader(urlencode(bodyParams))
headers["Content-Type"] = "application/x-www-form-urlencoded"
}
client := &http.Client{
Timeout: 5 * time.Second,
}
request, err := http.NewRequest(method, url, body)
if err != nil {
panic(err)
}
for k, v := range headers {
request.Header.Set(k, v)
}
response, err := client.Do(request)
if err != nil {
panic(err)
}
defer response.Body.Close()
bodyBytes, err := ioutil.ReadAll(response.Body)
if err != nil {
panic(err)
}
fmt.Println(string(bodyBytes))
return string(bodyBytes), nil
}
四、Cline 上配置自己開發的 MCP 服務
在 Cline 上添加 MCP 服務點擊右上角 “+” 號旁邊的 4 個小方塊按鈕即可進入。
裏面有默認的服務市場和已經安裝,我們在已經安裝這個 tab 中選擇 “Configure MCP Servers”,打開配置文件編輯,填寫以下內容:
{
"mcpServers": {
"helight-mcpServers": {
"command": "/Users/helightxu/aillm/mcpgolangtest/mcp-server",
"args": [],
"env": {},
"disabled": false,
"autoApprove": [
"current_weather",
"current_time"
]
}
}
}
主要就是 MCP 服務的名稱,裏面服務二進制地址等配置。這裏還有一個 “autoApprove” 配置項,這個是在執行命令的時候是否需要人工二次確認還是自動確認。配置之後就可以看到下圖的內容了。
這裏還可以點擊下面的 “Restart Server”,對服務進行重啓,重啓之後也會獲取最新的服務工具信息。在你重新編譯 MCP 服務之後,這裏一般需要點擊重啓一下。
五、案例測試
1. 時間獲取案例
這裏輸入 “當前東京的時間是多少”,執行過程如下,就會在大模型解析之後去調用我們的工具進行執行獲取結果,並且會對工具返回的結果使用大模型進行再次組織。
2. 天氣獲取案例
天氣這裏也是,提問:“深圳的天氣怎麼樣”,在大模型分析之後就會調用我們的天氣獲取工具進行天氣信息獲取,獲取之後再使用大模型進行信息組織和展示。
這裏是對信息的再次組織
五、MCP 的優勢和典型應用場景
1. MCP 的核心優勢
從系統集成和開放集成的角度來看,我認爲 MCP 會帶來以下的突破。
-
打破數據孤島:通過統一協議連接異構系統(如本地文檔、雲服務),減少大量的適配代碼開發量。
-
雙向動態交互:支持實時請求 - 響應和主動通知(如 WebSocket),相比傳統 API 的靜態交互更靈活。
-
隱私與安全:
-
數據隔離:敏感操作(如醫療數據處理)在本地 Server 完成,無需向 LLM 提供商暴露密鑰。
-
權限控制:Server 可自主定義訪問範圍,防止越權操作。
-
開發效率提升: 開發者只需關注業務邏輯,無需重複實現通信層,例如通過 Python SDK 快速構建天氣查詢服務。
-
MCP 目前比較典型的應用場景
我認爲目前 MCP 的一些典型應用場景會在以下幾個方面,不過這個應該發展會很快,未來也許會有更爲複雜的應用場景出現。
- 智能助手增強
MCP 可以顯著增強智能助手的能力,使其能夠:
-
訪問實時信息(如天氣、新聞、股票價格等)
-
執行復雜計算
-
查詢和操作數據庫
-
控制外部設備和系統
- 企業知識管理
在企業環境中,MCP 可以幫助:
-
構建智能知識庫
-
實現跨部門數據共享
-
自動化文檔處理和分析
-
提供個性化的員工支持
六、總結
我認爲這種模式應該是 AI 應用的趨勢,不做大模型,而是做大模型、內容信息和工具之間的一種膠水層,是一種思考模式的實現。感覺未來大廠的這些工具都會向這個方向發展。在特定的領域之內,結合大模型、具體場景信息和相關工具,思考組織執行方式和流程,最終完成一個自動化大規模計算的複雜任務。比如複雜的 k8s 集羣運維,複雜大規模的數據分析。
多智能體協作系統應該是未來的大趨勢。而 MCP 在多智能體系統中的應用是其最強大的特性之一。通過 MCP,多個 AI 智能體可以協同工作,各自負責不同的任務,並通過標準化的接口進行通信。
多智能體可能的一個架構:
多智能體系統
多智能體系統
├── 協調者智能體(Coordinator Agent)
├── 專家智能體 1(Expert Agent 1)
├── 專家智能體 2(Expert Agent 2)
├── ...
└── 專家智能體 N(Expert Agent N)
六、參考
- https://mp.weixin.qq.com/s/JmPxMBRZa8UhsIOQVgupLw
- https://guangzhengli.com/blog/zh/model-context-protocol/
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/mhihJ-cRuT81PdBzAFdX2Q