package main import ( "context" "encoding/json" "log" "os" "os/signal" "syscall" "github.com/creachadair/jrpc2" "github.com/creachadair/jrpc2/channel" "github.com/creachadair/jrpc2/handler" ) // EchoRequest is the request structure for the echo method type EchoRequest struct { Message string `json:"message"` } // EchoResponse is the response structure for the echo method type EchoResponse struct { Message string `json:"message"` Status string `json:"status"` } // Echo is a simple method that echoes back the message func Echo(ctx context.Context, req *EchoRequest) (*EchoResponse, error) { log.Printf("Echo method called with message: %s", req.Message) return &EchoResponse{ Message: req.Message, Status: "success", }, nil } // ErrorRequest is the request structure for the error method type ErrorRequest struct { Code int `json:"code"` Message string `json:"message"` } // Error is a method that always returns an error func Error(ctx context.Context, req *ErrorRequest) (interface{}, error) { log.Printf("Error method called with code: %d, message: %s", req.Code, req.Message) return nil, &jrpc2.Error{ Code: jrpc2.Code(req.Code), Message: req.Message, } } // Sum calculates the sum of an array of numbers func Sum(ctx context.Context, numbers []float64) (float64, error) { log.Printf("Sum method called with numbers: %v", numbers) var sum float64 for _, num := range numbers { sum += num } return sum, nil } // ToolsCallRequest handles a tools/call request type ToolsCallRequest struct { Name string `json:"name"` Parameters json.RawMessage `json:"parameters"` } type ToolsCallResponse struct { Result json.RawMessage `json:"result"` } // ToolsCall handles a tools/call request func ToolsCall(ctx context.Context, req *ToolsCallRequest) (*ToolsCallResponse, error) { log.Printf("tools/call method called with name: %s, parameters: %s", req.Name, string(req.Parameters)) // Echo back the parameters as the result return &ToolsCallResponse{ Result: req.Parameters, }, nil } func main() { // Set up signal handling for graceful shutdown ctx, cancel := context.WithCancel(context.Background()) defer cancel() sigs := make(chan os.Signal, 1) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) go func() { sig := <-sigs log.Printf("Received signal %v, shutting down...", sig) cancel() }() // Create a JSON-RPC server that communicates over stdin/stdout log.Println("Starting mock MCP adapter") // Define the methods methods := map[string]handler.Func{ "echo": handler.New(Echo), "error": handler.New(Error), "sum": handler.New(Sum), "tools/call": handler.New(ToolsCall), } // Create a server with the defined methods srv := jrpc2.NewServer(handler.Map(methods), &jrpc2.ServerOptions{ AllowPush: true, Logger: jrpc2.StdLogger(log.New(os.Stderr, "[jrpc2] ", log.LstdFlags)), }) // Create a channel for communication over stdin/stdout ch := channel.Line(os.Stdin, os.Stdout) // Start the server and wait for it to exit log.Println("Server ready to handle requests") if err := srv.Start(ch); err != nil { log.Fatalf("Server failed to start: %v", err) } // Wait for the server to exit or for context cancellation select { case <-ctx.Done(): log.Println("Context canceled, shutting down server") srv.Stop() } log.Println("Server exited gracefully") }