当前位置: 首页 > news >正文

MCP SDK 示例一

交互

Say Hi

服务端提供一个 SayHi 的 Tool,然后客户端请求该 Tool,得到 Tool 结果

package mainimport ("context""fmt""log""github.com/modelcontextprotocol/go-sdk/mcp"
)type SayHiParams struct {Name string `json:"name"`
}func SayHi(ctx context.Context, req *mcp.CallToolRequest, args SayHiParams) (*mcp.CallToolResult, any, error) {return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: "Hi " + args.Name},},}, nil, nil
}func main() {ctx := context.Background()clientTransport, serverTransport := mcp.NewInMemoryTransports()server := mcp.NewServer(&mcp.Implementation{Name: "greeter", Version: "v0.0.1"}, nil)mcp.AddTool(server, &mcp.Tool{Name: "greet", Description: "say hi"}, SayHi)serverSession, err := server.Connect(ctx, serverTransport, nil)if err != nil {log.Fatal(err)}client := mcp.NewClient(&mcp.Implementation{Name: "client"}, nil)clientSession, err := client.Connect(ctx, clientTransport, nil)if err != nil {log.Fatal(err)}res, err := clientSession.CallTool(ctx, &mcp.CallToolParams{Name:      "greet",Arguments: map[string]any{"name": "user"},})if err != nil {log.Fatal(err)}fmt.Println(res.Content[0].(*mcp.TextContent).Text)clientSession.Close()serverSession.Wait()// Output: Hi user
}

sdk中用了InMemoryTransports,它实际是一个 Pipe
Pipe 创建一个同步的(synchronous)、内存中的全双工(full duplex)网络连接;连接的两端均实现了 [Conn] 接口
一端的读取操作会与另一端的写入操作相匹配,数据直接在两端之间复制;不存在内部缓冲

// NewInMemoryTransports returns two [InMemoryTransports] that connect to each
// other.
func NewInMemoryTransports() (*InMemoryTransport, *InMemoryTransport) {c1, c2 := net.Pipe()return &InMemoryTransport{c1}, &InMemoryTransport{c2}
}

客户端 list 服务端提供的 tool

客户端代码

go build -o listfeatures main.go
./listfeatures go run github.com/modelcontextprotocol/go-sdk/examples/server/hello
package mainimport ("context""flag""fmt""iter""log""os""os/exec""github.com/modelcontextprotocol/go-sdk/mcp"
)func main() {flag.Parse()args := flag.Args()if len(args) == 0 {fmt.Fprintf(os.Stderr, "Usage: listfeatures <command> [<args>]")fmt.Fprintf(os.Stderr, "List all features for a stdio MCP server")fmt.Fprintln(os.Stderr)fmt.Fprintf(os.Stderr, "Example: listfeatures npx @modelcontextprotocol/server-everything")os.Exit(2)}ctx := context.Background()cmd := exec.Command(args[0], args[1:]...)client := mcp.NewClient(&mcp.Implementation{Name: "mcp-client", Version: "v1.0.0"}, nil)cs, err := client.Connect(ctx, &mcp.CommandTransport{Command: cmd}, nil)if err != nil {log.Fatal(err)}defer cs.Close()printSection("tools", cs.Tools(ctx, nil), func(t *mcp.Tool) string { return t.Name })printSection("resources", cs.Resources(ctx, nil), func(r *mcp.Resource) string { return r.Name })printSection("resource templates", cs.ResourceTemplates(ctx, nil), func(r *mcp.ResourceTemplate) string { return r.Name })printSection("prompts", cs.Prompts(ctx, nil), func(p *mcp.Prompt) string { return p.Name })
}func printSection[T any](name string, features iter.Seq2[T, error], featName func(T) string) {fmt.Printf("%s:\n", name)for feat, err := range features {if err != nil {log.Fatal(err)}fmt.Printf("\t%s\n", featName(feat))}fmt.Println()
}

对应的服务端代码

// The hello server contains a single tool that says hi to the user.
//
// It runs over the stdio transport.package mainimport ("context""log""github.com/modelcontextprotocol/go-sdk/mcp"
)func main() {// Create a server with a single tool that says "Hi".server := mcp.NewServer(&mcp.Implementation{Name: "greeter"}, nil)// Using the generic AddTool automatically populates the input and output// schema of the tool.//// The schema considers 'json' and 'jsonschema' struct tags to get argument// names and descriptions.// schema 考虑 json 和 jsonschema tag,来获取参数 name 和其 descriptiontype args struct {Name string `json:"name" jsonschema:"the person to greet"`}mcp.AddTool(server, &mcp.Tool{Name:        "greet",Description: "say hi",}, func(ctx context.Context, req *mcp.CallToolRequest, args args) (*mcp.CallToolResult, any, error) {return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: "Hi " + args.Name},},}, nil, nil})// server.Run runs the server on the given transport.//// In this case, the server communicates over stdin/stdout.if err := server.Run(context.Background(), &mcp.StdioTransport{}); err != nil {log.Printf("Server failed: %v", err)}
}

注意,这里用了范型 AddTool,自动推导 In、Out

// AddTool adds a tool and typed tool handler to the server.
//
// If the tool's input schema is nil, it is set to the schema inferred from the
// In type parameter, using [jsonschema.For]. The In type parameter must be a
// map or a struct, so that its inferred JSON Schema has type "object".
//
// For tools that don't return structured output, Out should be 'any'.
// Otherwise, if the tool's output schema is nil the output schema is set to
// the schema inferred from Out, which must be a map or a struct.
//
// It is a convenience for s.AddTool(ToolFor(t, h)).
func AddTool[In, Out any](s *Server, t *Tool, h ToolHandlerFor[In, Out]) {s.AddTool(ToolFor(t, h))
}// A ToolHandlerFor handles a call to tools/call with typed arguments and results.
type ToolHandlerFor[In, Out any] func(context.Context, *CallToolRequest, In) (*CallToolResult, Out, error)

引导

服务端主动向客户端请求补充信息,并通过标准化的交互完成数据收集

import ("context""fmt""log""github.com/google/jsonschema-go/jsonschema""github.com/modelcontextprotocol/go-sdk/mcp"
)func main() {ctx := context.Background()clientTransport, serverTransport := mcp.NewInMemoryTransports()// Create serverserver := mcp.NewServer(&mcp.Implementation{Name: "config-server", Version: "v1.0.0"}, nil)serverSession, err := server.Connect(ctx, serverTransport, nil)if err != nil {log.Fatal(err)}// Create client with elicitation handler// Note: Never use elicitation for sensitive data like API keys or passwordsclient := mcp.NewClient(&mcp.Implementation{Name: "config-client", Version: "v1.0.0"}, &mcp.ClientOptions{// 这是客户端接收并响应服务端信息请求的核心回调函数ElicitationHandler: func(ctx context.Context, request *mcp.ElicitRequest) (*mcp.ElicitResult, error) {fmt.Printf("Server requests: %s\n", request.Params.Message)// In a real application, this would prompt the user for input// Here we simulate user providing configuration datareturn &mcp.ElicitResult{Action: "accept",Content: map[string]any{"serverEndpoint": "https://api.example.com","maxRetries":     float64(3),"enableLogs":     true,},}, nil},})_, err = client.Connect(ctx, clientTransport, nil)if err != nil {log.Fatal(err)}// 服务端首先定义需要客户端提供的配置数据结构(通过 JSON Schema 描述)// 这个 Schema 用于告诉客户端:“需要提供哪些字段,格式是什么,是否必填”// Server requests user configuration via elicitationconfigSchema := &jsonschema.Schema{Type: "object",Properties: map[string]*jsonschema.Schema{"serverEndpoint": {Type: "string", Description: "Server endpoint URL"},"maxRetries":     {Type: "number", Minimum: ptr(1.0), Maximum: ptr(10.0)},"enableLogs":     {Type: "boolean", Description: "Enable debug logging"},},Required: []string{"serverEndpoint"},}// 服务端通过 serverSession.Elicit() 方法向客户端发送信息请求// MCP 协议会将该请求通过内存传输层发送给客户端result, err := serverSession.Elicit(ctx, &mcp.ElicitParams{Message:         "Please provide your configuration settings", // 人类可读的请求说明RequestedSchema: configSchema, // 上述定义的Schema,约束客户端返回的数据格式})if err != nil {log.Fatal(err)}// 客户端的 ElicitationHandler 回调函数接收到服务端的请求,参数 *mcp.ElicitRequest 包含服务端的 Message 和 RequestedSchema// 打印引导信息,并鼓励用户交互输入(本示例中,ElicitationHandler 中直接写死了交互结果,并向服务端返回消息)// 从消息中取出正确的参数if result.Action == "accept" {fmt.Printf("Configuration received: Endpoint: %v, Max Retries: %.0f, Logs: %v\n",result.Content["serverEndpoint"],result.Content["maxRetries"],result.Content["enableLogs"])}// Output:// Server requests: Please provide your configuration settings// Configuration received: Endpoint: https://api.example.com, Max Retries: 3, Logs: true
}// ptr is a helper function to create pointers for schema constraints
func ptr[T any](v T) *T {return &v
}

仅服务端 - CompletionHandler的配置

package mainimport ("context""fmt""log""github.com/modelcontextprotocol/go-sdk/mcp"
)// This example demonstrates the minimal code to declare and assign
// a CompletionHandler to an MCP Server's options.
func main() {// Define your custom CompletionHandler logic.myCompletionHandler := func(_ context.Context, req *mcp.CompleteRequest) (*mcp.CompleteResult, error) {// In a real application, you'd implement actual completion logic here.// For this example, we return a fixed set of suggestions.var suggestions []stringswitch req.Params.Ref.Type {case "ref/prompt":suggestions = []string{"suggestion1", "suggestion2", "suggestion3"}case "ref/resource":suggestions = []string{"suggestion4", "suggestion5", "suggestion6"}default:return nil, fmt.Errorf("unrecognized content type %s", req.Params.Ref.Type)}return &mcp.CompleteResult{Completion: mcp.CompletionResultDetails{HasMore: false,Total:   len(suggestions),Values:  suggestions,},}, nil}// Create the MCP Server instance and assign the handler.// No server running, just showing the configuration._ = mcp.NewServer(&mcp.Implementation{Name: "server"}, &mcp.ServerOptions{CompletionHandler: myCompletionHandler,})log.Println("MCP Server instance created with a CompletionHandler assigned (but not running).")log.Println("This example demonstrates configuration, not live interaction.")
}

附录

MCP 提供的 ClientOptions

type ClientOptions struct {// Handler for sampling.// Called when a server calls CreateMessage.CreateMessageHandler func(context.Context, *CreateMessageRequest) (*CreateMessageResult, error)// Handler for elicitation.// Called when a server requests user input via Elicit.ElicitationHandler func(context.Context, *ElicitRequest) (*ElicitResult, error)// Handlers for notifications from the server.ToolListChangedHandler      func(context.Context, *ToolListChangedRequest)PromptListChangedHandler    func(context.Context, *PromptListChangedRequest)ResourceListChangedHandler  func(context.Context, *ResourceListChangedRequest)ResourceUpdatedHandler      func(context.Context, *ResourceUpdatedNotificationRequest)LoggingMessageHandler       func(context.Context, *LoggingMessageRequest)ProgressNotificationHandler func(context.Context, *ProgressNotificationClientRequest)// If non-zero, defines an interval for regular "ping" requests.// If the peer fails to respond to pings originating from the keepalive check,// the session is automatically closed.KeepAlive time.Duration
}

MCP 之 ElicitationHandler 信息诱导

ElicitationHandler(通常翻译为 “启发 / 诱导处理器”)是用于处理 “客户端需要补充信息” 场景的核心组件
主要解决服务端在处理请求时发现信息不完整的问题。

当服务端在处理客户端请求(如工具调用、内容补全)时,发现必要信息缺失(例如调用 “天气查询” 工具但缺少 “城市名称” 参数),ElicitationHandler 会触发一个 “信息诱导流程”

信息诱导流程

  • 服务端通过该处理器生成 “补充信息的请求”(如 “请提供需要查询天气的城市名称”)
  • 客户端接收后,收集所需信息并重新发起请求
  • 服务端利用补充的信息完成后续处理

典型使用场景

工具调用参数不全
客户端调用需要多参数的工具(如 “转账” 工具需要 “金额”“收款人” 参数),但只提供了部分参数。此时 ElicitationHandler 会自动生成提示,要求客户端补充缺失的参数。

补全请求上下文不足
客户端请求生成 “邮件回复”,但未提供原始邮件内容。服务端通过 ElicitationHandler 询问:“请提供需要回复的原始邮件内容,以便生成合适的回复。”

权限或格式校验不通过
若客户端提供的参数格式错误(如 “日期” 参数应为 YYYY-MM-DD 却传入 2024/8/31),处理器会返回格式要求,引导客户端修正。

与其他处理器的区别

和 CompletionHandler 不同:CompletionHandler 专注于生成补全内容,而 ElicitationHandler 专注于 “发现信息缺失并引导补充”
和 CallToolHandler 不同:工具处理器执行具体功能,而 ElicitationHandler 是在功能执行前的 “信息校验与补充流程”

工作流程示例

  • 客户端调用 greet 工具,但未提供 name 参数
  • 服务端检测到参数缺失,触发 ElicitationHandler
  • 处理器生成诱导信息:{“elicit”: {“field”: “name”, “message”: “请提供您的姓名以便问候”}}
  • 客户端接收后,补充 name: “Bob” 并重新调用
  • 服务端完成处理,返回 Hi Bob

总结

ElicitationHandler 是 MCP 中实现 “交互式信息补充” 的关键,让服务端能动态引导客户端完善请求,避免因信息不全导致的请求失败,提升交互的流畅性

http://www.dtcms.com/a/359857.html

相关文章:

  • Spring MVC 九大组件源码深度剖析(六):HandlerExceptionResolver - 异常处理的艺术
  • 第八章 光照
  • 蓝牙AOA智慧仓储管理系统:实现仓储数字化升级的精准定位解决方案
  • 解决IDEA 2025.2升级报错:Scannning Files to Index卡住问题分析与修复
  • python复杂代码如何让ide自动推导提示内容
  • 【系列12】端侧AI:构建与部署高效的本地化AI模型 第11章:边缘设备与IoT部署
  • Wi-Fi技术——网络安全
  • 现代软件系统架构:前端、后端、数据库、部署、算法与AI学习的结构与交互分析
  • 学习:uniapp全栈微信小程序vue3后台(8)
  • USB虚拟化应用5:VirtualFIDO2 虚拟硬件安全密钥,智能卡,yubico,支持X,FB,GITHUB等各种网站双重认证,让你的账户登录绝对安全
  • LeetCode 1855.下标对中的最大距离
  • a3002盘式制动器刹车cad➕三维图➕设计说明书
  • DreamForge
  • leetcode 268 丢失的数字
  • 前端学习——JavaScript基础
  • Vue2 与 Vue3 路由钩子的区别及用法详解
  • 电科金仓KingbaseES V9数据库:国产数据库的自主创新与行业实践深度解析
  • Nginx四层负载均衡实战指南
  • Redis 7.0 高性能缓存架构设计与优化
  • Spring Data JPA 派生查询方法命名速查表
  • 【51单片机】【protues仿真】基于51单片机智能晾衣架系统
  • git中使用SSH的配置
  • 从零开始搭建使用 TDengine:新用户快速上手指南
  • STAR法则
  • Encoder编码器
  • kafka服务端架构总览
  • sublime MAC系统快捷键及常见问题
  • 深入理解Nginx反向代理及其应用
  • 【机器学习学习笔记】numpy基础
  • Library cache lock常见案例分析(一)