如何使用golang实现一个MCP-Client

之前的几篇文章中,主要跟大家分享了如何创建一个MCP-Server,以及如何调试MCP-Server。今天,我们就写一个简单MCP-Client来与之前写的pgsql-mcp-server的交互示例,来看看MCP-Client是如何和MCP-Server进行通信的。

1. 如何编写一个MCP-Client

在官方For Client Developers的快速开始中,提供了几种编程语言的示例代码,但是示例代码写死了mcp服务的提供要么是js文件要么是py文件,所以分享一个通用的可以连接任意代码编写的MCP-Client的是有点价值的,这里我仅示例客户端和mcp-server的通信,和LLM大模型的交互后面再做分享。

1.1 读取MCP-Server的配置文件

我们看到许多MCP Host(例如cline、Claude for Desktop等)都是提供一个配置文件,供使用者将其要用到的MCP-Server进行配置连接,所以,我们先来读取配置文件。

这里仿照cline的配置,给出示例代码:

go 复制代码
type MCPConfig struct {
	McpServers map[string]MCPServer `json:"mcpServers"`
}

type MCPServer struct {
	Command     string            `json:"command"`
	Args        []string          `json:"args"`
	Env         map[string]string `json:"env"`
	Disabled    bool              `json:"disabled"`
	AutoApprove []string          `json:"autoApprove"`
}

这里忽略掉读取文件的代码,直接给出要解析的配置文件:

go 复制代码
	configData := `{
		"mcpServers": {
		  "pgsql-mcp-server": {
			"command": "C:\\Users\\Administrator\\go\\bin\\sql-mcp-server.exe",
			"args": [],
			"env": {
			  "GOPATH": "C:\\Users\\Administrator\\go",
			  "GOMODCACHE": "C:\\Users\\Administrator\\go\\pkg\\mod",
			  // 省略其他环境变量
			  ...
			  "DB_SSLMODE": "disable"
			},
			"disabled": false,
			"autoApprove": []
		  }
		}
	  }`

	// 解析配置文件
	var config MCPConfig
	if err := json.Unmarshal([]byte(configData), &config); err != nil {
		fmt.Printf("Failed to parse config file: %v\n", err)
		return
	}

1.2 初始化MCP-Client

通过上面的步骤,我们以及知道了待连接的pgsql-mcp-server配置信息,我们现在可以初始化一个MCP-Client,将其配置传入到MCP-Client中。

go 复制代码
	c, err := client.NewStdioMCPClient(
		mcpServerConfig.Command,
		envSlice,
		mcpServerConfig.Args...,
	)
	if err != nil {
		fmt.Printf("Failed to createa new stdio-based MCP client: %v\n", err)
		return
	}
	defer c.Close()

1.3 连接MCP-Server

现在可以与pgsql-mcp-server 进行通信,这里我们使用MCP-Client的Initialize方法进行初始化,并打印出pgsql-mcp-server的版本信息。

go 复制代码
	initRequest := mcp.InitializeRequest{}
	initRequest.Params.ProtocolVersion = mcp.LATEST_PROTOCOL_VERSION
	initRequest.Params.ClientInfo = mcp.Implementation{
		Name:    "mcp-client",
		Version: "1.0.0",
	}

	initResult, err := c.Initialize(ctx, initRequest)
	if err != nil {
		log.Fatalf("Failed to initialize: %v", err)
	}
    	fmt.Printf(
		"Initialized with server: %s %s\n\n",
		initResult.ServerInfo.Name,
		initResult.ServerInfo.Version,
	)

如果没有问题的话,将会打印出如下内容:

vbscript 复制代码
Initializing client...
Initialized with server: sql-mcp-server 🚀 1.0.0

2. 交互

MCP-Client主要和MCP-Server交互Resources,Prompts Tools,Sampling以及Roots,这里由于我上次写的pgsql-mcp-server只提供了Tools,所以下面仅说明List Tools和Call Tools的两种场景。

2.1 List Tools

go 复制代码
toolsRequest := mcp.ListToolsRequest{}
	tools, err := c.ListTools(ctx, toolsRequest)
	if err != nil {
		log.Fatalf("Failed to list tools: %v", err)
	}
	for _, tool := range tools.Tools {
		fmt.Printf("- %s: %s\n", tool.Name, tool.Description)
	}
	fmt.Println()

上面代码成功运行后,将会打印出如下内容:

  • create_table: Create a new table in the postgres database
  • list_tables: List all user tables in the database
  • read_query: Execute a SELECT query on the postgres database
  • write_query: Execute an INSERT, UPDATE, or DELETE query on the postgres database

2.1 Call Tools

上面已经给出了四个tool,接下来就实现对应的交互代码即可

go 复制代码
// List tables
	fmt.Println("Listing tables...")
	listTableRequest := mcp.CallToolRequest{
		Request: mcp.Request{
			Method: "tools/call",
		},
	}
	listTableRequest.Params.Name = "list_tables"

	result, err := c.CallTool(ctx, listTableRequest)
	if err != nil {
		log.Fatalf("Failed to list allowed directories: %v", err)
	}
	printToolResult(result)

	// Read table
	fmt.Println("Reading table...")
	readTableRequest := mcp.CallToolRequest{}
	readTableRequest.Params.Name = "read_query"
	readTableRequest.Params.Arguments = map[string]interface{}{
		"query": "SELECT * FROM users LIMIT 10",
	}

	result, err = c.CallTool(ctx, readTableRequest)
	if err != nil {
		log.Fatalf("Failed to create directory: %v", err)
	}
	printToolResult(result)
go 复制代码
func printToolResult(result *mcp.CallToolResult) {
	for _, content := range result.Content {
		if textContent, ok := content.(mcp.TextContent); ok {
			fmt.Println(textContent.Text)
		} else {
			jsonBytes, _ := json.MarshalIndent(content, "", "  ")
			fmt.Println(string(jsonBytes))
		}
	}
}

上面代码成功运行后,将会打印出如下内容:

less 复制代码
Listing tables...
Tables: [students articles]

Creating table...
Table created successfully

Writing table...
Operation successful. Rows affected: 1

Reading table...
Query results: [map[created_at:2025-03-29 03:52:12.545937 +0000 +0000 email:john@example.com id:1 name:John Doe]]

3. 总结

ok,以上就是一个简单的MCP-Client的示例代码,通过这个示例,大家可以了解如何使用golang来编写一个MCP-Client,以及如何使用MCP-Client与MCP-Server进行通信,完整的示例代码我已经提交到github,请参考demo_for_AI

相关推荐
程序员辉哥10 小时前
在Cursor中通过SSH MCP运维自己的服务器
ssh·cursor·mcp
后端小肥肠14 小时前
【n8n入门系列】输入抖音分享链接,3步自动提无水印视频 + 文案,小白也能上手!
agent·deepseek·mcp
梁梁梁梁较瘦16 小时前
边界检查消除(BCE,Bound Check Elimination)
go
梁梁梁梁较瘦17 小时前
指针
go
梁梁梁梁较瘦17 小时前
内存申请
go
半枫荷17 小时前
七、Go语法基础(数组和切片)
go
beyond阿亮18 小时前
nacos支持MCP Server注册与发现
java·python·ai·nacos·mcp
todoitbo1 天前
我用 TRAE 做了一个不一样的 MySQL MCP
数据库·mysql·adb·ai工具·mcp·trae·mysql-mcp
大模型真好玩1 天前
低代码Agent开发框架使用指南(五)—Coze消息卡片详解
人工智能·coze·mcp
梁梁梁梁较瘦1 天前
Go工具链
go