如何使用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:[email protected] id:1 name:John Doe]]

3. 总结

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

相关推荐
缘友一世1 天前
开篇:MCP理论理解和学习
学习·mcp·模型上下文协议
Clown952 天前
Go语言爬虫系列教程(三)HTML解析技术
爬虫·go·goquery
fashia2 天前
Java转Go日记(五十六):gin 渲染
开发语言·后端·golang·go·gin
白总Server2 天前
Golang基于 Swagger + JWT + RBAC 的认证授权中间件设计
linux·运维·服务器·架构·go·bash·jwt
fashia3 天前
Java转Go日记(五十四):gin路由
开发语言·后端·golang·go·html·gin
带刺的坐椅3 天前
Solon AI 正试发布(支持 java8+,RAG,MCP)
ai·solon·java8·rag·mcp
不买Huracan不改名4 天前
Tare使用MCP|Win11安装UV
uv·mcp·tare
救救孩子把4 天前
打造一个支持MySQL查询的MCP同步插件:Java实现
java·mysql·mcp·stdio
fashia5 天前
Java转Go日记(三十六):简单的分布式
开发语言·分布式·后端·zookeeper·golang·go
deephub5 天前
5个开源MCP服务器:扩展AI助手能力,高效处理日常工作
人工智能·深度学习·大语言模型·mcp