MCP Go包装器
强制+验证中间件 mcp走 工具操作员。
mcp go v0.43+处理模式生成(mcp.WithInputSchema[T]())和类型化参数绑定(request.BindArguments())本地。这个包装器位于mcp-go和您的处理程序之间,用于添加LLM在实践中需要的两件事:
- 批量类型强制 --LLM定期发送
"20"而不是20。在mcp-go绑定字符串参数之前,包装器会将字符串参数强制转换为结构中声明的类型。 - go游乐场/验证器运行时验证 --
validate:"required,min=3,email"在绑定后,在处理程序运行之前检查标签。
它在Raw mcp go上添加了什么
| 关注 | 原始mcp go | 带包装 |
|---|---|---|
| 模式生成 | mcp.WithInputSchema[T]() | 相同(mcp代表参加) |
| 参数绑定 | request.BindArguments(&args) | 相同(mcp代表参加) |
| 字符串到类型强制 | 无-- "20" 未能绑定到 int | 绑定前自动 |
| 运行时验证 | 无-编写自己的检查 | validate 结构标签 |
| 格式化错误 | 原始错误 | 人类可读的验证消息 |
安装
go get github.com/aleksadvaisly/mcp-go-wrapper快速开始
定义参数结构体 json 和 validate 标签:
type GreetArgs struct {
Name string `json:"name" validate:"required,min=1"`
Format string `json:"format" validate:"omitempty,oneof=formal casual"`
}三种连接方式:
1.便利性API
在一次调用中生成模式+强制+验证。最适合大多数情况。
mcpwrapper.Register[GreetArgs](w, "greet", "Greet someone",
func(ctx context.Context, req mcp.CallToolRequest, args GreetArgs) (*mcp.CallToolResult, error) {
return mcp.NewToolResultText("Hello " + args.Name), nil
},
)Register 电话 mcp.WithInputSchema[T]() 对于模式,然后用 TypedHandler[T] 用于强制和验证。
2.直接mcp-go集成(中间件模式)
使用 TypedHandler 直接与mcp go合作 AddTool。当您想要完全控制工具选项时很有用。
validate := validator.New()
mcpServer.AddTool(
mcp.NewTool("greet",
mcp.WithDescription("Greet someone"),
mcp.WithInputSchema[GreetArgs](),
),
mcpwrapper.TypedHandler[GreetArgs](validate, handler),
)TypedHandler 返回a server.ToolHandlerFunc 它强制、绑定、验证,然后调用您的类型化处理程序。
3.结构化输出
退货 structuredContent 通过 mcp.NewToolResultStructuredOnly。当MCP客户端期望机器可读的结果时使用。
type CalcArgs struct {
A int `json:"a" validate:"required"`
B int `json:"b" validate:"required"`
Op string `json:"operation" validate:"required,oneof=add subtract"`
}
type CalcResult struct {
Result float64 `json:"result"`
}
mcpServer.AddTool(
mcp.NewTool("calculate",
mcp.WithDescription("Basic arithmetic"),
mcp.WithInputSchema[CalcArgs](),
),
mcpwrapper.StructuredHandler[CalcArgs, CalcResult](validate,
func(ctx context.Context, req mcp.CallToolRequest, args CalcArgs) (CalcResult, error) {
switch args.Op {
case "add":
return CalcResult{Result: float64(args.A + args.B)}, nil
case "subtract":
return CalcResult{Result: float64(args.A - args.B)}, nil
default:
return CalcResult{}, fmt.Errorf("unsupported operation: %s", args.Op)
}
},
),
)验证标签
运行时验证使用 去操场/验证器.标签上 validate 字段标签。
| 标签 | 描述 | 示例 |
|---|---|---|
required | 字段不能为零值 | validate:"required" |
min= | 最小长度/值 | validate:"min=3" |
max= | 最大长度/值 | validate:"max=50" |
email | 有效的电子邮件格式 | validate:"email" |
url | 有效的URL格式 | validate:"url" |
oneof= | 值必须是列表中的一个 | validate:"oneof=red blue green" |
gte= | 大于或等于 | validate:"gte=0" |
lte= | 小于或等于 | validate:"lte=100" |
omitempty | 如果为空,则跳过验证 | validate:"omitempty,email" |
用逗号组合标签:
Age int `json:"age" validate:"required,gte=0,lte=120"`结构标签
包装器使用两种标签类型:
json--字段名称映射。标准Go JSON标签。由mcp-go's使用BindArguments以及包装器的强制逻辑。validate--运行时验证规则。绑定后由go游乐场/验证器处理。
模式生成(jsonschema 标签、描述、枚举、最小/最大约束)由mcp-go处理 WithInputSchema[T](),使用 invopop/jsonschema 引擎盖下。
架构修补: omitempty 从中删除字段 required
invopop/jsonschema 将所有结构体字段标记为 required 默认情况下。这对于可选字段是错误的——MCP客户端将拒绝缺少这些字段的调用。使用时,包装器会自动修复此问题 Register[T] 或 RegisterCobra[T].
字段结束的规则 required:
| 场景 | In required? | 为什么 |
|---|---|---|
validate:"required,min=1" | 是 | 明确要求 |
validate:"omitempty,gte=1" | 否 | 显式省略 |
validate:"omitempty,required" | 是的 | required 赢得青睐 omitempty |
validate:"email" (无住宿) | 是 | 无住宿=需要住宿 |
validate:"required_if=Mode adv" | 是的 | required_if 不是 required (完全匹配) |
没有 validate 标签全部 | 是 | 只有omitempty从必填项中删除 |
json:"field,omitempty" | 没有 | invopop/jsonschema 也尊重json格式 |
例子:
type SearchArgs struct {
Query string `json:"query" validate:"required,min=1"` // -> required
Limit int `json:"limit" validate:"omitempty,gte=1"` // -> NOT required
Offset int `json:"offset" validate:"omitempty"` // -> NOT required
Format string `json:"format" validate:"email"` // -> required (no omitempty)
Mode string `json:"mode" validate:"required_if=Format json"` // -> required (required_if != required)
}结果模式 required: ["query", "format", "mode"].
当所有字段都是可选的时,架构包含 required: [] (空数组,不为null或缺失)。这是MCP协议所要求的。
此修补程序仅适用于使用 Register[T] 或 RegisterCobra[T]。如果您使用 TypedHandler 直接与 mcp.NewTool,您自己管理模式。
结合模式和验证标签的示例:
type CreateUserArgs struct {
Email string `json:"email" jsonschema:"description=User email" validate:"required,email"`
Age int `json:"age" jsonschema:"minimum=0,maximum=120" validate:"required,gte=0,lte=120"`
Role string `json:"role" jsonschema:"enum=admin,enum=user" validate:"required,oneof=admin user"`
}这里 jsonschema mcp-go读取工具模式的标签; validate 标签在调用时由包装器读取。
Cobra集成
RegisterCobra 从中提取工具名称 cmd.Use 以及来自 cmd.Short (回到 cmd.Long).
greetCmd := &cobra.Command{
Use: "greet",
Short: "Greet someone by name",
}
mcpwrapper.RegisterCobra[GreetArgs](w, greetCmd,
func(ctx context.Context, req mcp.CallToolRequest, args GreetArgs) (*mcp.CallToolResult, error) {
return mcp.NewToolResultText("Hello " + args.Name), nil
},
)整合模式: serve 命令
在将MCP支持添加到现有CLI应用程序时,创建一个新的 serve 子命令,而不是修改主应用程序:
var serveCmd = &cobra.Command{
Use: "serve",
Short: "Start MCP server",
Run: func(cmd *cobra.Command, args []string) {
log.SetOutput(os.Stderr)
mcpServer := server.NewMCPServer(
"my-app", "1.0.0",
server.WithInstructions("Describe what your server does."),
)
w := mcpwrapper.New(mcpServer)
mcpwrapper.RegisterCobra[MyArgs](w, myCmd, myHandler)
if err := server.ServeStdio(mcpServer); err != nil {
log.Fatal(err)
}
},
}这可以在添加MCP功能的同时使CLI正常工作:
./my-app command--作为常规CLI运行./my-app serve--启动MCP服务器以进行AI集成
关键:标准输出与标准错误
MCP协议使用stdio(stdin/stdout)进行JSON-RPC通信。任何非协议输出到stdout都会破坏连接。
做 --对所有日志记录和调试输出使用stderr:
log.SetOutput(os.Stderr)
fmt.Fprintln(os.Stderr, "message")不要 --将任何内容写入stdout:
fmt.Println("message") // BREAKS PROTOCOL
log.Println("message") // BREAKS PROTOCOL (default log writes to stderr, but verify)
fmt.Printf("debug: %v", x) // BREAKS PROTOCOLMCP客户端在stdout上只需要有效的JSON-RPC。如果您混合使用日志行:
Starting server...
{"jsonrpc":"2.0","id":1,"method":"tools/list"}客户端解析失败,连接中断。
关键:服务器。使用说明()
始终添加 server.WithInstructions() 创建MCP服务器时:
mcpServer := server.NewMCPServer(
"my-app", "1.0.0",
server.WithInstructions("Describe your server's purpose and capabilities."),
)没有指令,人工智能代理将无法理解服务器的用途或何时使用其工具。指令出现在MCP中 initialize 响应,以及客户端如何发现服务器的功能。
建筑
+-------------------+
| MCP Client | (Claude, Cursor, etc.)
| (JSON-RPC) |
+--------+----------+
|
v
+-------------------+
| mcp-go | Schema, transport, binding
| (protocol layer) |
+--------+----------+
|
v
+-------------------+
| mcp-go-wrapper | 绑定->验证->处理程序。
### 便利功能
func RegisterT any func RegisterCobraT any error
`Register` 电话 `mcp.NewTool` 随着 `WithInputSchema[T]()` 并包裹处理器。 `RegisterCobra` 执行相同的操作,但从Cobra命令派生名称和描述。
## 对于AI代理
阅读此README后,您应该能够将MCP支持自主集成到Go CLI应用程序中。
步骤:
1. **分析目标CLI** --识别命令、它们的参数和业务逻辑
1. **创建参数结构** --定义类型化结构 `json`, `jsonschema`,以及 `validate` 每个命令的标签
1. **实现处理程序** --写 `mcp.TypedToolHandlerFunc[T]` 调用现有命令逻辑的函数
1. **添加一个 `serve` 子命令** --创建一个新的Cobra命令来启动MCP服务器(不要修改主应用程序入口点)
1. **注册工具** --使用 `mcpwrapper.Register[T]()` 或 `mcpwrapper.RegisterCobra[T]()` 公开命令
1. **设置服务器** --用以下方式初始化 `server.WithInstructions()`,配置stdio传输,设置 `log.SetOutput(os.Stderr)`
示例 `serve` 命令:
var serveCmd = &cobra.Command{ Use: "serve", Short: "Start MCP server", Run: func(cmd *cobra.Command, cliArgs []string) { log.SetOutput(os.Stderr)
mcpServer := server.NewMCPServer( "my-app", "1.0.0", server.WithInstructions("Describe your server here."), ) w := mcpwrapper.New(mcpServer)
mcpwrapper.RegisterSearchArgs mcpwrapper.RegisterCreateArgs
if err := server.ServeStdio(mcpServer); err != nil { log.Fatal(err) } }, }
经办人签名:
func searchHandler(ctx context.Context, req mcp.CallToolRequest, args SearchArgs) (*mcp.CallToolResult, error) { results := doSearch(args.Query, args.Limit) return mcp.NewToolResultText(formatResults(results)), nil }
## 依赖项
- [mark3labs/mcp go](https://github.com/mark3labs/mcp-go) --MCP协议实现(v0.43+)
- [去操场/验证器](https://github.com/go-playground/validator) --结构验证
- [spf13/眼镜蛇](https://github.com/spf13/cobra) --CLI框架(可选,仅需要 `RegisterCobra`)
## 许可证
麻省理工学院
## 贡献
欢迎捐款。请确保:
- 测试通过(`go test ./...`)
- 代码已格式化(`go fmt ./...`)
- 示例仍然有效
## 学分
建立在优秀之上 [mcp走](https://github.com/mark3labs/mcp-go) Mark3 Labs的图书馆。