上手 MCP 官方 Go SDK:一份面向實戰的入門指南

大家好,我是 Tony Bai。

隨着大型語言模型(LLM)的能力邊界不斷擴展,“function calling”或 “tool use” 已成爲釋放其潛力的關鍵。MCP(Model Context Protocol)正是爲此而生,它定義了一套標準的、與模型無關的通信規範,使得任何應用都能以 “工具” 的形式被 LLM 調用。

長期以來,mcp 官方都沒有發佈 go-sdk,Go 社區也一直在使用像 mark3labs/mcp-go 這樣的流行的第三方庫。直到 Google Go 團隊安排專人協助 mcp 組織進行了 Go SDK 的設計

7 月初,該 Go SDK 正式以modelcontextprotocol/go-sdk倉庫的形式對外開源發佈,這是 Go 語言在這一浪潮中的一個里程碑事件。它的意義遠超一個普通的庫:

簡而言之,它不僅僅是一個工具,更是 Go 語言與 AI 模型世界之間的一座標準化橋樑。

MCP 服務架構:多種通信模式

MCP 協議設計了靈活的通信方式,以適應不同的部署場景。官方 Go SDK 對此提供了出色的支持。主要包括以下幾種類型:

核心概念速覽

儘管我們在此不深入探討其完整的設計文檔,但理解以下幾個核心概念對於後續的實踐至關重要:

Server 定義了 “能做什麼”,而 Session 則是 “正在與誰通信” 的實例。這種解耦設計爲構建靈活、可擴展的服務提供了基礎。

實戰:構建三種典型的 MCP 服務

現在,讓我們動手構建幾個實用的 MCP 服務,來體驗官方 SDK 的強大功能。

場景一:基礎工具服務 (Greeter)

這是最經典的 “Hello, World” 場景,通過 stdio 運行,用於展示如何定義一個簡單的工具。

完整代碼:greeter/main.go

// mcp-go-sdk/greeter/main.go
package main

import (
"context"
"fmt"
"log"

"github.com/modelcontextprotocol/go-sdk/mcp"
)

// HiParams 定義了工具的輸入參數,強類型保證
type HiParams struct {
 Name string`json:"name"`
}

// SayHi 是工具的具體實現
func SayHi(ctx context.Context, _ *mcp.ServerSession, params *mcp.CallToolParamsFor[HiParams]) (*mcp.CallToolResultFor[any], error) {
 resultText := fmt.Sprintf("Hi %s, welcome to the Go MCP world!", params.Arguments.Name)
return &mcp.CallToolResultFor[any]{
  Content: []mcp.Content{
   &mcp.TextContent{Text: resultText},
  },
 }, nil
}

func main() {
// 1. 創建 Server 實例
 server := mcp.NewServer("greeter-server""1.0.0", nil)

// 2. 添加工具
// NewServerTool 利用泛型和反射自動生成輸入 schema
 server.AddTools(
  mcp.NewServerTool("greet""Say hi to someone", SayHi),
 )

// 3. 通過 StdioTransport 運行服務,它會監聽標準輸入/輸出
 log.Println("Greeter server running over stdio...")
if err := server.Run(context.Background(), mcp.NewStdioTransport()); err != nil {
  log.Fatalf("Server run failed: %v", err)
 }
}

在不依賴任何特殊客戶端的情況下,我們可以通過管道向這個基於 stdio 的服務發送一系列原生的 JSON-RPC 消息,來模擬完整的客戶端握手和工具調用流程。

步驟一:運行服務併發送請求序列

打開你的終端,執行以下命令。這行命令會使用 printf 來確保每個 JSON 對象都以換行符分隔,模擬一個完整的會話流程:

  1. 發送 initialize 請求,啓動會話。

  2. 發送 initialized 通知,確認會話建立。

  3. 發送 tools/call 請求,調用 greet 工具。

在 greeter 目錄下執行下面命令:

printf '%s\n' \
  '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"clientInfo":{"name":"test-cli","version":"0.1"},"protocolVersion":"2025-03-26"}}' \
  '{"jsonrpc":"2.0","method":"notifications/initialized","params":{}}' \
  '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"greet","arguments":{"name":"Go MCP Enthusiast"}}}' \
  | go run main.go

預期輸出:

服務會處理這三個消息,並對兩個有 ID 的請求(initialize 和 tools/call)作出響應。你將看到兩個 JSON-RPC 響應對象被打印到標準輸出(順序可能會因併發處理而不同,但內容是固定的):

2025/07/08 17:05:46 Greeter server running over stdio...
{"jsonrpc":"2.0","id":1,"result":{"capabilities":{"completions":{},"logging":{},"prompts":{"listChanged":true},"resources":{"listChanged":true},"tools":{"listChanged":true}},"protocolVersion":"2025-03-26","serverInfo":{"name":"greeter-server","version":"1.0.0"}}}
{"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"Hi Go MCP Enthusiast, welcome to the Go MCP world!"}]}}

看到這兩個響應,證明我們的 Greeter 服務已經成功地完成了握手並正確響應了工具調用。

場景二:文件系統服務 (File System Server)

這個場景也通過 stdio 運行,展示瞭如何通過 Resource 機制,安全地向 LLM 暴露本地文件系統的讀寫能力。

// fileserver/main.go

package main

import (
"context"
"fmt"
"log"
"os"
"path/filepath"

"github.com/modelcontextprotocol/go-sdk/mcp"
)

func main() {
 server := mcp.NewServer("filesystem-server""1.0.0", nil)

 pwd, err := os.Getwd()
if err != nil {
  log.Fatalf("Failed to get current directory: %v", err)
 }
 log.Printf("File server serving from directory: %s", pwd)

// 使用我們自己實現的 File Handler
 handler := createFileHandler(pwd)

// 添加一個虛構的資源,用於列出目錄內容
 server.AddResources(&mcp.ServerResource{
  Resource: &mcp.Resource{
   URI:         "mcp://fs/list",
   Name:        "list_files",
   Description: "List all non-directory files in the current directory.",
  },
  Handler: listDirectoryHandler(pwd),
 })

// 添加一個資源模板,用於讀取指定的文件
 server.AddResourceTemplates(&mcp.ServerResourceTemplate{
  ResourceTemplate: &mcp.ResourceTemplate{
   Name:        "read_file",
   URITemplate: "file:///{+filename}",
   Description: "Read a specific file from the directory. 'filename' is the relative path to the file.",
  },
  Handler: handler,
 })

 log.Println("File system server running over stdio...")
if err := server.Run(context.Background(), mcp.NewStdioTransport()); err != nil {
  log.Fatalf("Server run failed: %v", err)
 }
}

// createFileHandler 是一個簡化的、用於演示的 ResourceHandler 工廠函數。
func createFileHandler(baseDir string) mcp.ResourceHandler {
returnfunc(ctx context.Context, ss *mcp.ServerSession, params *mcp.ReadResourceParams) (*mcp.ReadResourceResult, error) {
// 注意:在生產環境中,這裏必須調用 ss.ListRoots() 來獲取客戶端授權的
// 根目錄,並進行嚴格的安全檢查。
// 爲了讓這個入門示例能用簡單的管道命令驗證,我們暫時省略了這個雙向調用。
  requestedPath := filepath.Join(baseDir, filepath.FromSlash(params.URI[len("file:///"):]))

  data, err := os.ReadFile(requestedPath)
if err != nil {
   if os.IsNotExist(err) {
    returnnil, mcp.ResourceNotFoundError(params.URI)
   }
   returnnil, fmt.Errorf("failed to read file: %w", err)
  }

return &mcp.ReadResourceResult{
   Contents: []*mcp.ResourceContents{
    {URI: params.URI, MIMEType: "text/plain", Text: string(data)},
   },
  }, nil
 }
}

// listDirectoryHandler 是一個自定義的 ResourceHandler,用於實現列出目錄的功能
func listDirectoryHandler(dir string) mcp.ResourceHandler {
returnfunc(ctx context.Context, ss *mcp.ServerSession, params *mcp.ReadResourceParams) (*mcp.ReadResourceResult, error) {
// 同樣,爲簡化本地驗證,暫時省略對 ss.ListRoots() 的調用。

  entries, err := os.ReadDir(dir)
if err != nil {
   returnnil, fmt.Errorf("failed to read directory: %w", err)
  }

var fileList string
for _, e := range entries {
   if !e.IsDir() {
    fileList += e.Name()"\n"
   }
  }
if fileList == "" {
   fileList = "(The directory is empty or contains no files)"
  }

return &mcp.ReadResourceResult{
   Contents: []*mcp.ResourceContents{
    {URI: params.URI, MIMEType: "text/plain", Text: fileList},
   },
  }, nil
 }
}

文件服務同樣需要完整的握手流程。我們將用與上面類似的方式來驗證其功能。

步驟一:準備測試文件

首先,在你的項目根目錄下創建一個簡單的文本文件。

echo "Hello from the File System MCP Server!" > my-test-file.txt

步驟二:驗證 “列出文件” 功能

我們發送包含 initializeinitialized 和 resources/read 的請求序列。

在 fileserver 下執行下面命令:

printf '%s\n' \
  '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"clientInfo":{"name":"test-cli","version":"0.1"},"protocolVersion":"2025-03-26"}}' \
  '{"jsonrpc":"2.0","method":"notifications/initialized","params":{}}' \
  '{"jsonrpc":"2.0","id":2,"method":"resources/read","params":{"uri":"mcp://fs/list"}}' \
  | go run main.go

預期輸出:

你將看到 initialize 的響應,以及 resources/read 的響應,後者包含了目錄文件列表。

2025/07/08 18:13:47 File system server running over stdio...
{"jsonrpc":"2.0","id":1,"result":{"capabilities":{"completions":{},"logging":{},"prompts":{"listChanged":true},"resources":{"listChanged":true},"tools":{"listChanged":true}},"protocolVersion":"2025-03-26","serverInfo":{"name":"filesystem-server","version":"1.0.0"}}}
{"jsonrpc":"2.0","id":2,"result":{"contents":[{"uri":"mcp://fs/list","mimeType":"text/plain","text":"go.mod\ngo.sum\nmain.go\nmy-test-file.txt\n"}]}}

步驟三:驗證 “讀取文件” 功能

現在,我們發送請求序列來讀取 my-test-file.txt 的內容。

printf '%s\n' \
  '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"clientInfo":{"name":"test-cli","version":"0.1"},"protocolVersion":"2025-03-26"}}' \
  '{"jsonrpc":"2.0","method":"notifications/initialized","params":{}}' \
  '{"jsonrpc":"2.0","id":3,"method":"resources/read","params":{"uri":"file:///my-test-file.txt"}}' \
  | go run main.go

預期輸出:

除了 initialize 的響應外,你將看到包含文件內容的 resources/read 響應。

2025/07/08 18:15:12 File server serving from directory: /Users/tonybai/go/src/github.com/bigwhite/experiments/mcp-go-sdk/fileserver
2025/07/08 18:15:12 File system server running over stdio...
{"jsonrpc":"2.0","id":1,"result":{"capabilities":{"completions":{},"logging":{},"prompts":{"listChanged":true},"resources":{"listChanged":true},"tools":{"listChanged":true}},"protocolVersion":"2025-03-26","serverInfo":{"name":"filesystem-server","version":"1.0.0"}}}
{"jsonrpc":"2.0","id":3,"result":{"contents":[{"uri":"file:///my-test-file.txt","mimeType":"text/plain","text":"Hello from the File System MCP Server\n"}]}}

步驟四:清理

測試完成後,可以刪除測試文件。

rm my-test-file.txt

場景三:多路複用 HTTP 服務 (Multi-Service HTTP Server)

這個場景展示瞭如何使用 StreamableHTTPHandler 在單個 HTTP 端點上提供多個不同的 MCP 服務。

完整代碼:httpserver/main.go

package main

import (
"context"
"fmt"
"log"
"net/http"

"github.com/modelcontextprotocol/go-sdk/mcp"
)

// HiParams 和 SayHi 函數與場景一相同
type HiParams struct{ Name string`json:"name"` }
func SayHi(ctx context.Context, _ *mcp.ServerSession, params *mcp.CallToolParamsFor[HiParams]) (*mcp.CallToolResultFor[any], error) {
 resultText := fmt.Sprintf("Hi %s, this response is from the HTTP server!", params.Arguments.Name)
return &mcp.CallToolResultFor[any]{
  Content: []mcp.Content{&mcp.TextContent{Text: resultText}},
 }, nil
}

// AddParams 和 Add 工具的實現
type AddParams struct{ A, B int }
func Add(_ context.Context, _ *mcp.ServerSession, params *mcp.CallToolParamsFor[AddParams]) (*mcp.CallToolResultFor[any], error) {
 result := params.Arguments.A + params.Arguments.B
return &mcp.CallToolResultFor[any]{
  Content: []mcp.Content{&mcp.TextContent{Text: fmt.Sprintf("The sum is: %d", result)}},
 }, nil
}

func main() {
// 1. 創建 Greeter 服務實例
 greeterServer := mcp.NewServer("greeter-service""1.0", nil)
 greeterServer.AddTools(mcp.NewServerTool("greet""Say hi", SayHi))

// 2. 創建 Math 服務實例
 mathServer := mcp.NewServer("math-service""1.0", nil)
 mathServer.AddTools(mcp.NewServerTool("add""Add two integers", Add))

// 3. 創建 StreamableHTTPHandler
 handler := mcp.NewStreamableHTTPHandler(func(request *http.Request) *mcp.Server {
  log.Printf("Routing request for URL: %s\n", request.URL.Path)
switch request.URL.Path {
case"/greeter":
   return greeterServer
case"/math":
   return mathServer
default:
   returnnil// 返回 nil 將導致 404 Not Found
  }
 }, nil)

// 4. 啓動標準的 Go HTTP 服務器
 addr := ":8080"
 log.Printf("Multi-service MCP server listening at http://localhost%s\n", addr)
if err := http.ListenAndServe(addr, handler); err != nil {
  log.Fatalf("HTTP server failed: %v", err)
 }
}

與基於 stdio 的簡單服務不同,驗證 Streamable HTTP 服務使用 curl 等工具會非常繁瑣。這是因爲 MCP 是一個有狀態的協議,要求客戶端在發送工具調用之前,必須先完成一個包含 initialize 請求和 initialized 通知的多步 “握手” 流程來建立會話。

一個簡單的 curl 命令無法管理這種有狀態的交互。因此,最理想的驗證方式是使用一個真正的 MCP 客戶端。我們將在下一節構建這樣一個客戶端——agent,然後用集成了大模型的它來統一驗證我們創建的所有三個服務,包括這個 HTTP 服務。

集成大模型:讓 Go Agent 直接成爲 MCP 客戶端

在前面的章節中,我們成功構建了三種不同類型的 MCP 服務。現在,是時候將它們與 AI 大模型(以 DeepSeek 爲例)集成,構建一個能夠調度這些 mcp server 工具的智能 Agent 了。

一個常見的思路可能是創建一個通用的命令行工具(CLI)來調用這些服務,然後讓我們的 Go Agent 程序去執行這個 CLI。然而,既然我們的 Agent 本身就是用 Go 編寫的,一個更優雅、更高效、更符合 Go 語言習慣的方式是:讓 Agent 程序直接導入 modelcontextprotocol/go-sdk,將自己作爲原生的 MCP 客戶端來與服務通信。

這種方法避免了不必要的進程開銷和數據序列化,使得整個系統更加內聚和高性能。接下來,我們將編寫這樣一個 Go Agent。

Part 1: 編寫 Go Agent 程序

這個程序將承擔所有角色:它既是與 DeepSeek 模型對話的主循環,也是調用我們 MCP 服務的客戶端。

準備工作

  1. 安裝 OpenAI Go SDK:go get github.com/openai/openai-go

  2. 獲取 DeepSeek API Key,並設置爲環境變量:export DEEPSEEK_API_KEY="your-api-key"

完整代碼:agent/main.go

// agent/main.go

package main

import (
"context"
"encoding/json"
"fmt"
"log"
"os"
"os/exec"
"strings"

"github.com/modelcontextprotocol/go-sdk/mcp"
"github.com/openai/openai-go"
"github.com/openai/openai-go/option"
)

// serverConfig 結構體用於管理不同 MCP 服務的連接信息
type serverConfig struct {
 ServerCmd string// 用於 stdio 服務
 HTTPAddr  string// 用於 http 服務
}

// toolRegistry 映射對 LLM 友好的工具別名到其服務配置
var toolRegistry = map[string]serverConfig{
"greet":      {ServerCmd: "go run ../greeter/main.go"},
"add":        {HTTPAddr: "http://localhost:8080/math"},
"list_files": {ServerCmd: "go run ../fileserver/main.go"},
"read_file":  {ServerCmd: "go run ../fileserver/main.go"},
}

// invokeMCPTool 是 Agent 的核心函數,負責直接與 MCP 服務通信
func invokeMCPTool(toolAlias string, arguments map[string]interface{}) (string, error) {
 config, ok := toolRegistry[toolAlias]
if !ok {
return"", fmt.Errorf("unknown tool alias: %s", toolAlias)
 }

// 1. 將 LLM 友好的別名和參數,轉換爲真正的 MCP 請求
 mcpToolName := toolAlias
 mcpArguments := arguments
if toolAlias == "list_files" {
  mcpToolName = "resources/read"
  mcpArguments = map[string]interface{}{"uri""mcp://fs/list"}
 } elseif toolAlias == "read_file" {
  mcpToolName = "resources/read"
if filename, ok := arguments["filename"].(string); ok {
   mcpArguments = map[string]interface{}{"uri""file:///" + filename}
  } else {
   return"", fmt.Errorf("tool 'read_file' requires a 'filename' argument")
  }
 }

// 2. 創建 MCP 客戶端實例
 client := mcp.NewClient("go-agent""1.0", nil)

// 3. 根據配置選擇並創建 Transport
var transport mcp.Transport
if config.ServerCmd != "" {
  cmdParts := strings.Fields(config.ServerCmd)
  transport = mcp.NewCommandTransport(exec.Command(cmdParts[0], cmdParts[1:]...))
 } else {
  transport = mcp.NewStreamableClientTransport(config.HTTPAddr, nil)
 }

// 4. 授權客戶端訪問本地文件系統(僅對文件服務調用有效)
 client.AddRoots(&mcp.Root{URI: "file://./"})

// 5. 連接到服務器,建立會話
 ctx := context.Background()
 session, err := client.Connect(ctx, transport)
if err != nil {
return"", fmt.Errorf("failed to connect to MCP server for tool %s: %w", toolAlias, err)
 }
defer session.Close() // 每次調用都是一個獨立的會話,確保關閉

// 6. 執行調用並處理結果
var resultText string
if mcpToolName == "resources/read" {
  res, err := session.ReadResource(ctx, &mcp.ReadResourceParams{
   URI: mcpArguments["uri"].(string),
  })
if err != nil {
   return"", fmt.Errorf("ReadResource failed: %w", err)
  }
var sb strings.Builder
for _, c := range res.Contents {
   sb.WriteString(c.Text)
  }
  resultText = sb.String()
 } else {
  res, err := session.CallTool(ctx, &mcp.CallToolParams{
   Name:      mcpToolName,
   Arguments: mcpArguments,
  })
if err != nil {
   return"", fmt.Errorf("CallTool failed: %w", err)
  }
if res.IsError {
   return"", fmt.Errorf("tool execution failed: %s", res.Content[0].(*mcp.TextContent).Text)
  }
  resultText = res.Content[0].(*mcp.TextContent).Text
 }

return resultText, nil
}

func main() {
 apiKey := os.Getenv("DEEPSEEK_API_KEY")
if apiKey == "" {
  log.Fatal("DEEPSEEK_API_KEY environment variable not set.")
 }

 client := openai.NewClient(
  option.WithAPIKey(apiKey),
  option.WithBaseURL("https://api.deepseek.com/v1"),
 )

// 爲所有工具使用合法的名稱,特別是爲 `resources/read` 創建別名
 tools := []openai.ChatCompletionToolParam{
  {
   Function: openai.FunctionDefinitionParam{
    Name:        "greet",
    Description: openai.String("Say hi to someone."),
    Parameters: openai.FunctionParameters{
     "type""object""properties": map[string]interface{}{"name": map[string]string{"type""string""description""Name of the person to greet"}}"required": []string{"name"},
    },
   },
  },
  {
   Function: openai.FunctionDefinitionParam{
    Name:        "add",
    Description: openai.String("Add two integers."),
    Parameters: openai.FunctionParameters{
     "type""object""properties": map[string]interface{}{"A": map[string]string{"type""integer"}"B": map[string]string{"type""integer"}}"required": []string{"A""B"},
    },
   },
  },
  {
   Function: openai.FunctionDefinitionParam{
    Name:        "list_files",
    Description: openai.String("List all non-directory files in the current project directory."),
    Parameters:  openai.FunctionParameters{"type""object""properties": map[string]interface{}{}},
   },
  },
  {
   Function: openai.FunctionDefinitionParam{
    Name:        "read_file",
    Description: openai.String("Read the content of a specific file."),
    Parameters: openai.FunctionParameters{
     "type""object""properties": map[string]interface{}{"filename": map[string]string{"type""string""description""The name of the file to read."}}"required": []string{"filename"},
    },
   },
  },
 }

 messages := []openai.ChatCompletionMessageParamUnion{
  openai.SystemMessage("You are a helpful assistant with access to local tools. You must call tools by using the tool_calls response format. Don't make assumptions about what values to plug into functions. Ask for clarification if a user request is ambiguous."),
  openai.UserMessage("Hi, can you greet my friend Alex, add 5 and 7, and then list the files in my project?"),
 }

 ctx := context.Background()

for i := 0; i < 5; i++ {
  log.Println("--- Sending request to DeepSeek ---")

  resp, err := client.Chat.Completions.New(ctx, openai.ChatCompletionNewParams{Model: "deepseek-chat", Messages: messages, Tools: tools})
if err != nil {
   log.Fatalf("ChatCompletion error: %v\n", err)
  }
iflen(resp.Choices) == 0 {
   log.Fatal("No choices returned from API")
  }

  msg := resp.Choices[0].Message
  messages = append(messages, msg.ToParam())

if msg.ToolCalls != nil {
   for _, toolCall := range msg.ToolCalls {
    functionName := toolCall.Function.Name
    var arguments map[string]interface{}
    if err := json.Unmarshal([]byte(toolCall.Function.Arguments), &arguments); err != nil {
     log.Fatalf("Failed to unmarshal function arguments: %v", err)
    }

    log.Printf("--- LLM wants to call tool: %s with args: %v ---\n", functionName, arguments)

    // 直接調用我們的 Go 函數,該函數內建了 MCP 客戶端邏輯
    toolResult, err := invokeMCPTool(functionName, arguments)
    if err != nil {
     log.Printf("Tool call failed: %v\n", err)
     toolResult = fmt.Sprintf("Error executing tool: %v", err)
    }

    log.Printf("--- Tool result: ---\n%s\n---------------------\n", toolResult)

    messages = append(messages, openai.ToolMessage(toolResult, toolCall.ID))
   }
   continue
  }

  log.Println("--- Final response from LLM ---")
  log.Println(msg.Content)
return
 }
 log.Println("Reached max conversation turns.")
}

注:上述代碼使用了 OpenAI 的 function calling api,不過即便不用 function calling api,通過 prompt 依然可以實現 mcp server 接口的調用 (需要自行解析 response),大家可以自行實現一下。

Part 2: 集成驗證

現在,我們的 agent 程序已經是一個功能齊全的、內建了 MCP 客戶端的智能體。讓我們來驗證它的工作流程。

  1. 啓動 httpserver: agent 會通過 HTTP 調用 math 服務,所以我們必須先在後臺運行它。

    go run ./httpserver/main.go & HTTP_PID=$!
  2. 創建測試文件: 爲文件服務準備一個可供讀取的文件。

    echo "This file will be read by our Go AI Agent." > agent-test.txt
  3. 運行 agent 程序: 確保你的 DEEPSEEK_API_KEY 已經設置。

    go run ./agent/main.go

預期的輸出流程:

你的終端將清晰地展示 AI Agent 的思考和行動鏈。它直接在內部與各個 MCP 服務進行高效的 Go-to-Go 通信。

$DEEPSEEK_API_KEY=<your_deepseek_api_key> go run main.go
2025/07/08 19:17:42 --- Sending request to DeepSeek ---
2025/07/08 19:17:53 --- LLM wants to call tool: greet with args: map[name:Alex] ---
2025/07/08 19:17:53 --- Tool result: ---
Hi Alex, welcome to the Go MCP world!
---------------------
2025/07/08 19:17:53 --- LLM wants to call tool: add with args: map[A:5 B:7] ---
2025/07/08 19:17:53 --- Tool result: ---
The sum is: 12
---------------------
2025/07/08 19:17:53 --- LLM wants to call tool: list_files with args: map[] ---
2025/07/08 19:17:53 --- Tool result: ---
go.mod
go.sum
main.go

---------------------
2025/07/08 19:17:53 --- Sending request to DeepSeek ---
2025/07/08 19:18:07 --- Final response from LLM ---
2025/07/08 19:18:07 Here's what you asked for:

1. **Greeting for Alex**: Hi Alex, welcome to the Go MCP world!
2. **Addition of 5 and 7**: The sum is 12.
3. **Files in your project**:
   - `go.mod`
   - `go.sum`
   - `main.go`

Let me know if you'd like to do anything else!

4. 清理

kill $HTTP_PID
rm agent-test.txt

小結

通過本次從零到一的實踐,我們不僅學習瞭如何使用 modelcontextprotocol/go-sdk 構建支持不同通信協議的 MCP 服務,更重要的是,我們探索並實現了將 Go Agent 程序直接作爲原生 MCP 客戶端的實踐。

這種直接通過庫調用的內聚架構,相比於通過外部 CLI 工具進行解耦的方式,充分發揮了 Go 語言的優勢:

modelcontextprotocol/go-sdk 不僅僅是一個協議的實現,它更像一個宣言:Go 語言憑藉其出色的併發模型、強大的類型系統和簡潔的工程哲學,完全有能力成爲構建下一代高性能、高可靠性 AI Agent 和工具化應用的首選後端語言。

雖然官方 SDK 仍在快速迭代中,但其展現出的潛力和清晰的設計哲學已經足夠令人振奮。我們鼓勵所有對 Go 和 AI 結合感興趣的開發者,立即上手體驗。這個 SDK 無疑將成爲連接你的 Go 程序與廣闊智能模型世界之間最堅固、最標準的橋樑。

本文涉及源碼可以在這裏下載 - https://github.com/bigwhite/experiments/tree/master/mcp-go-sdk


本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/JvPuH1q0QMm-Ak-F1TeYvg