mcp折腾记录

先总结一下吧。。mcp本身实际上是一种很愚蠢的实现方式,消耗巨量token的同时,又没办法保证所需功能的切实实现,本质上还是和llm这样一个黑箱情况有关,人们只能依靠提示词去碰运气靠大模型可以理解对

贴一下自己写的mcp调用wrk进行压测的工具

一边看代码一边理一下MCP的概念

1
2
3
4
5
6
7
s := server.NewMCPServer(
"wrk Benchmarking Tool",
"1.0.0",
server.WithResourceCapabilities(true, true),
server.WithLogging(),
server.WithRecovery(),
)

首先就是Server,也就是提供mcp服务的客户端,进行相应配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
wrkTool := mcp.NewTool("wrk",
mcp.WithDescription("wrk is an HTTP benchmarking tool"),
mcp.WithString("url",
mcp.Required(),
mcp.Description("URL to test"),
),
mcp.WithNumber("threads",
mcp.Required(),
mcp.Description("Number of threads to use"),
),
mcp.WithNumber("connections",
mcp.Required(),
mcp.Description("Number of connections to open"),
),
mcp.WithString("duration",
mcp.Required(),
mcp.Description("Test duration (e.g.: 30s, 1m)"),
),
mcp.WithString("method",
mcp.Description("HTTP method (GET, POST, PUT, DELETE)"),
mcp.DefaultString("GET"),
),
mcp.WithString("body",
mcp.Description("Request body for POST/PUT methods"),
mcp.DefaultString(""),
),
)

然后就是mcp的核心——工具

这里是重中之重,需要恰当的词汇去描述这个工具的用途,以及各个参数的意义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
s.AddTool(wrkTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
// Get parameters
url := request.Params.Arguments["url"].(string)
threads := int(request.Params.Arguments["threads"].(float64))
connections := int(request.Params.Arguments["connections"].(float64))
duration := request.Params.Arguments["duration"].(string)
method := strings.ToUpper(request.Params.Arguments["method"].(string))
body := request.Params.Arguments["body"].(string)

// Check if we need to use a Lua script for non-GET methods
needsLuaScript := method != "GET"

// Build wrk command
cmdArgs := []string{
"-t", fmt.Sprintf("%d", threads),
"-c", fmt.Sprintf("%d", connections),
"-d", duration,
}

// Add Lua script for non-GET methods
if needsLuaScript {
scriptPath, err := createLuaScript(method, body)
if err != nil {
return mcp.NewToolResultError(fmt.Sprintf("Error creating Lua script: %v", err)), nil
}
defer os.Remove(scriptPath) // Clean up temporary script file

cmdArgs = append(cmdArgs, "-s", scriptPath)
}

// Add URL at the end
cmdArgs = append(cmdArgs, url)

// Execute command and get output
cmd := exec.CommandContext(ctx, "wrk", cmdArgs...)
output, err := cmd.CombinedOutput()
if err != nil {
// Check if the error is because wrk command does not exist
if strings.Contains(err.Error(), "executable file not found") {
return mcp.NewToolResultError("wrk command not found, please ensure wrk tool is installed"), nil
}
return mcp.NewToolResultError(fmt.Sprintf("Error executing wrk command: %v", err)), nil
}

// Return result
return mcp.NewToolResultText(string(output)), nil
})

// Start server
if err := server.ServeStdio(s); err != nil {
fmt.Printf("Server Error: %v\\n", err)
}
}

这里就是实际工具的执行逻辑

本质就是获取参数,然后放到cmdArgs数组里,最后调用cmd命令去执行即可

像wrk就比较简单,-t -c -d都可以直接从关键词里获取,直接request.Params.Arguments即可

可wrk也有-s执行lua脚本的复杂逻辑,这部分就容易让大模型过载,比如如果要测试POST,PUT或者DELETE,正常情况下需要复杂的路由,复杂的逻辑,复杂的鉴权加密,token等各种情况,就需要添加复杂的逻辑实现

我现在编写也只能执行简单的POST接口测试,还没办法做到根据接口文档进行复杂测试

不过mcp可以看出,其实本质就是让大模型去读自己编写的工具,理解之后调用function call功能,把参数传进去

本质上没有任何改进的地方,就是做了一个规范约定而已


mcp折腾记录
http://example.com/2025/04/22/mcp折腾记录/
作者
WoodQ
发布于
2025年4月22日
许可协议