使用说明
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=<n> |
最小长度/值 | validate:"min=3" |
max=<n> |
最大长度/值 | validate:"max=50" |
email |
有效的电子邮件格式 | validate:"email" |
url |
有效的URL格式 | validate:"url" |
oneof=<vals> |
值必须是列表中的一个 | validate:"oneof=red blue green" |
gte=<n> |
大于或等于 | validate:"gte=0" |
lte=<n> |
小于或等于 | 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 PROTOCOL
MCP客户端在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 | <- Coercion + validation middleware
| (this library) |
+--------+----------+
|
v
+-------------------+
| Your Handler | Receives typed, validated args
+-------------------+
包装器是一个中间件层。它不会替换或包装mcp-go服务器,而是包装单个工具处理程序函数。
错误处理
验证错误
作为工具错误(非Go错误)返回,以便MCP客户端看到它们:
{
"content": [{"type": "text", "text": "validation failed: Name: is required; Format: must be one of: formal casual"}],
"isError": true
}
强制错误
{
"content": [{"type": "text", "text": "coercion failed: field age: strconv.ParseInt: parsing \"abc\": invalid syntax"}],
"isError": true
}
处理程序错误
对于 StructuredHandler,处理程序错误被包装为工具错误:
{
"content": [{"type": "text", "text": "handler error: division by zero"}],
"isError": true
}
对于 TypedHandler,错误处理由您的处理程序决定,因为它返回 (*mcp.CallToolResult, error) 直接。
API 参考
包装器
func New(mcpServer *server.MCPServer) *Wrapper
func NewWithValidator(mcpServer *server.MCPServer, v *validator.Validate) *Wrapper
New 使用默认验证器创建包装器。 NewWithValidator 允许您通过预配置 *validator.Validate (例如注册了自定义验证功能)。
中间件功能
func TypedHandler[T any](v *validator.Validate, handler mcp.TypedToolHandlerFunc[T]) server.ToolHandlerFunc
func StructuredHandler[TArgs any, TResult any](v *validator.Validate, handler mcp.StructuredToolHandlerFunc[TArgs, TResult]) server.ToolHandlerFunc
两者均返回a server.ToolHandlerFunc 它执行强制->绑定->验证->处理程序。
便利功能
func Register[T any](w *Wrapper, name, description string, handler mcp.TypedToolHandlerFunc[T])
func RegisterCobra[T any](w *Wrapper, cmd *cobra.Command, handler mcp.TypedToolHandlerFunc[T]) error
Register 电话 mcp.NewTool 随着 WithInputSchema[T]() 并包裹处理器。 RegisterCobra 执行相同的操作,但从Cobra命令派生名称和描述。
对于AI代理
阅读此README后,您应该能够将MCP支持自主集成到Go CLI应用程序中。
步骤:
- 分析目标CLI --识别命令、它们的参数和业务逻辑
- 创建参数结构 --定义类型化结构
json,jsonschema,以及validate每个命令的标签 - 实现处理程序 --写
mcp.TypedToolHandlerFunc[T]调用现有命令逻辑的函数 - 添加一个
serve子命令 --创建一个新的Cobra命令来启动MCP服务器(不要修改主应用程序入口点) - 注册工具 --使用
mcpwrapper.Register[T]()或mcpwrapper.RegisterCobra[T]()公开命令 - 设置服务器 --用以下方式初始化
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.Register[SearchArgs](w, "search", "Search documents", searchHandler)
mcpwrapper.Register[CreateArgs](w, "create", "Create a document", createHandler)
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 --MCP协议实现(v0.43+)
- 去操场/验证器 --结构验证
- spf13/眼镜蛇 --CLI框架(可选,仅需要
RegisterCobra)
许可证
麻省理工学院
贡献
欢迎捐款。请确保:
- 测试通过(
go test ./...) - 代码已格式化(
go fmt ./...) - 示例仍然有效
学分
建立在优秀之上 mcp走 Mark3 Labs的图书馆。
