之前的几篇文章中,主要跟大家分享了如何创建一个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。