暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

proto直接生成MCP-server:protoc-gen-go-mcp(3)

        在介绍完生成MCP server的三种使用方法后,发现介绍的都是STDIO协议的版本,要向使用SSE版本的MCP可以行吗?当然,使用过程中核心逻辑不变,只是入口改为NEWSSEServer即可,具体源码如下
    package main
    import (
        example "learn/langchain/protoc_gen_mcp/exp1/server"
        "github.com/mark3labs/mcp-go/server"
    )
    func main() {
        s := server.NewMCPServer(
            "My Server"// Server 名称
            "1.0.0",     // 版本号
        )
        example.RegisterExampleService(s, &example.Server{})
        // 创建基于 SSE 的服务器实例
        sseServer := server.NewSSEServer(s)
        // 启动服务器,监听指定端口(如 :8080)
        err := sseServer.Start(":8083")
        if err != nil {
            panic(err)
        }
    }
    定义完成后我们可以使用工具mcpinspector测试下是否成功
      nvm use v18.14.0
      npx @modelcontextprotocol/inspector
      启动后界面如下:
      在tools/list下面我们可以看到我们定义的mcp工具
        example_ExampleService_GetPerson
        call tool后返回如下
          {
          person:
            {
              name:
              "hello"
              id:
              123
              email:
              "example@example.com"
            }
          }
          当然也可以使用stdio协议,不过要配置对应的二机制文件路径。在使用的最后,我们分析下生成的.pb.mcp.go文件的源码
            // Code generated by protoc-gen-mcp-go. DO NOT EDIT.
            // source: example_service.proto
            package example_service_v1mcp
            import (
                example_service_v1 "learn/langchain/protoc_gen_mcp/exp1/example.service.v1"
            )
            import (
                "context"
                "github.com/mark3labs/mcp-go/mcp"
                mcpserver "github.com/mark3labs/mcp-go/server"
                "encoding/json"
                "google.golang.org/protobuf/encoding/protojson"
                "connectrpc.com/connect"
                grpc "google.golang.org/grpc"
            )
            var (
                ExampleService_GetPersonTool = mcp.Tool{Name: "example_ExampleService_GetPerson", Description: "", InputSchema: mcp.ToolInputSchema{Type: "", Properties: map[string]interface{}(nil), Required: []string(nil)}, RawInputSchema: json.RawMessage{0x7b0x220x700x720x6f0x700x650x720x740x690x650x730x220x3a0x7b0x220x6e0x610x6d0x650x220x3a0x7b0x220x740x790x700x650x220x3a0x220x730x740x720x690x6e0x670x220x7d0x7d0x2c0x220x720x650x710x750x690x720x650x640x220x3a0x5b0x5d0x2c0x220x740x790x700x650x220x3a0x220x6f0x620x6a0x650x630x740x220x7d}}
            )
            // ExampleServiceServer is compatible with the grpc-go server interface.
            type ExampleServiceServer interface {
                GetPerson(ctx context.Context, req *example_service_v1.PersonRequest) (*example_service_v1.PersonResponse, error)
            }
            func RegisterExampleServiceHandler(s *mcpserver.MCPServer, srv ExampleServiceServer) {
                s.AddTool(ExampleService_GetPersonTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
                    marshaled, err := json.Marshal(request.Params.Arguments)
                    if err != nil {
                        return nil, err
                    }
                    var req example_service_v1.PersonRequest
                    if err := (protojson.UnmarshalOptions{DiscardUnknown: true}).Unmarshal(marshaled, &req); err != nil {
                        return nil, err
                    }
                    resp, err := srv.GetPerson(ctx, &req)
                    if err != nil {
                        return nil, err
                    }
                    marshaled, err = (protojson.MarshalOptions{UseProtoNames: true, EmitDefaultValues: true}).Marshal(resp)
                    if err != nil {
                        return nil, err
                    }
                    return mcp.NewToolResultText(string(marshaled)), nil
                })
            }
            // ExampleServiceClient is compatible with the grpc-go client interface.
            type ExampleServiceClient interface {
                GetPerson(ctx context.Context, req *example_service_v1.PersonRequest, opts ...grpc.CallOption) (*example_service_v1.PersonResponse, error)
            }
            // ConnectExampleServiceClient is compatible with the connectrpc-go client interface.
            type ConnectExampleServiceClient interface {
                GetPerson(ctx context.Context, req *connect.Request[example_service_v1.PersonRequest]) (*connect.Response[example_service_v1.PersonResponse], error)
            }
            // ForwardToConnectExampleServiceClient registers a connectrpc client, to forward MCP calls to it.
            func ForwardToConnectExampleServiceClient(s *mcpserver.MCPServer, client ConnectExampleServiceClient) {
                s.AddTool(ExampleService_GetPersonTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
                    marshaled, err := json.Marshal(request.Params.Arguments)
                    if err != nil {
                        return nil, err
                    }
                    var req example_service_v1.PersonRequest
                    if err := (protojson.UnmarshalOptions{DiscardUnknown: true}).Unmarshal(marshaled, &req); err != nil {
                        return nil, err
                    }
                    resp, err := client.GetPerson(ctx, connect.NewRequest(&req))
                    if err != nil {
                        return nil, err
                    }
                    marshaled, err = (protojson.MarshalOptions{UseProtoNames: true, EmitDefaultValues: true}).Marshal(resp.Msg)
                    if err != nil {
                        return nil, err
                    }
                    return mcp.NewToolResultText(string(marshaled)), nil
                })
            }
            // ForwardToExampleServiceClient registers a gRPC client, to forward MCP calls to it.
            func ForwardToExampleServiceClient(s *mcpserver.MCPServer, client ExampleServiceClient) {
                s.AddTool(ExampleService_GetPersonTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
                    marshaled, err := json.Marshal(request.Params.Arguments)
                    if err != nil {
                        return nil, err
                    }
                    var req example_service_v1.PersonRequest
                    if err := (protojson.UnmarshalOptions{DiscardUnknown: true}).Unmarshal(marshaled, &req); err != nil {
                        return nil, err
                    }
                    resp, err := client.GetPerson(ctx, &req)
                    if err != nil {
                        return nil, err
                    }
                    marshaled, err = (protojson.MarshalOptions{UseProtoNames: true, EmitDefaultValues: true}).Marshal(resp)
                    if err != nil {
                        return nil, err
                    }
                    return mcp.NewToolResultText(string(marshaled)), nil
                })
            }
            可以看到,它核心有三个方法,分别对应的是本地实现MCP server,代理到connectrpc Server 和代理到grpc server
              RegisterExampleServiceHandler
              ForwardToConnectExampleServiceClient
              ForwardToExampleServiceClient
              三者实现的流程都是使用的AddTool添加MCP tool,然后将MCP的jsonrpc协议序列化为json,然后再反序列化为pb
                 s.AddTool(ExampleService_GetPersonTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
                        marshaled, err := json.Marshal(request.Params.Arguments)
                        if err != nil {
                            return nil, err
                        }
                        var req example_service_v1.PersonRequest
                        if err := (protojson.UnmarshalOptions{DiscardUnknown: true}).Unmarshal(marshaled, &req); err != nil {
                            return nil, err
                        }
                最后分别调用本地的server实现的方法或者使用connecrrpc的客户端转发请求,或者grpc的客户端转发请求。

                文章转载自golang算法架构leetcode技术php,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                评论