From 2d419b1e31e9868b027d158f2c603073bc0ee9d1 Mon Sep 17 00:00:00 2001 From: Tai Groot Date: Thu, 16 Oct 2025 02:34:25 -0400 Subject: [PATCH] update to use official libraries --- go.mod | 7 +- go.sum | 32 ++------ main.go | 243 ++++++++++++++++++++++++-------------------------------- 3 files changed, 115 insertions(+), 167 deletions(-) diff --git a/go.mod b/go.mod index fefd5a1..0850a8e 100644 --- a/go.mod +++ b/go.mod @@ -1,11 +1,10 @@ module github.com/taigrr/gotify-mcp -go 1.24.5 +go 1.25.2 -require github.com/mark3labs/mcp-go v0.33.0 +require github.com/modelcontextprotocol/go-sdk v1.0.0 require ( - github.com/google/uuid v1.6.0 // indirect - github.com/spf13/cast v1.7.1 // indirect + github.com/google/jsonschema-go v0.3.0 // indirect github.com/yosida95/uritemplate/v3 v3.0.2 // indirect ) diff --git a/go.sum b/go.sum index 6d7cb84..89026b2 100644 --- a/go.sum +++ b/go.sum @@ -1,26 +1,10 @@ -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= -github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mark3labs/mcp-go v0.33.0 h1:naxhjnTIs/tyPZmWUZFuG0lDmdA6sUyYGGf3gsHvTCc= -github.com/mark3labs/mcp-go v0.33.0/go.mod h1:rXqOudj/djTORU/ThxYx8fqEVj/5pvTuuebQ2RC7uk4= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= -github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/jsonschema-go v0.3.0 h1:6AH2TxVNtk3IlvkkhjrtbUc4S8AvO0Xii0DxIygDg+Q= +github.com/google/jsonschema-go v0.3.0/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE= +github.com/modelcontextprotocol/go-sdk v1.0.0 h1:Z4MSjLi38bTgLrd/LjSmofqRqyBiVKRyQSJgw8q8V74= +github.com/modelcontextprotocol/go-sdk v1.0.0/go.mod h1:nYtYQroQ2KQiM0/SbyEPUWQ6xs4B95gJjEalc9AQyOs= github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= diff --git a/main.go b/main.go index 145173a..3cbf305 100644 --- a/main.go +++ b/main.go @@ -5,11 +5,11 @@ import ( "context" "encoding/json" "fmt" + "log" "net/http" "os" - "github.com/mark3labs/mcp-go/mcp" - "github.com/mark3labs/mcp-go/server" + "github.com/modelcontextprotocol/go-sdk/mcp" ) type GotifyMessage struct { @@ -18,81 +18,55 @@ type GotifyMessage struct { Priority int `json:"priority,omitempty"` } -func getStringArg(args map[string]any, key string, defaultValue string) string { - if val, ok := args[key].(string); ok { - return val - } - return defaultValue +type SendMessageArgs struct { + Message string `json:"message" jsonschema:"The message content to send"` + Title string `json:"title,omitempty" jsonschema:"Optional title for the message"` + Priority float64 `json:"priority,omitempty" jsonschema:"Message priority (0-10 default: 5)"` } -func getNumberArg(args map[string]any, key string, defaultValue float64) float64 { - if val, ok := args[key].(float64); ok { - return val - } - return defaultValue +type AskForHelpArgs struct { + Context string `json:"context" jsonschema:"Context or description of what help is needed"` + Error string `json:"error,omitempty" jsonschema:"Optional error message or details"` +} + +type NotifyCompletionArgs struct { + Task string `json:"task" jsonschema:"Description of the completed task"` + Result string `json:"result,omitempty" jsonschema:"Optional result or outcome details"` +} + +type SummarizeActivityArgs struct { + Summary string `json:"summary" jsonschema:"Summary of activities or current status"` + Details string `json:"details,omitempty" jsonschema:"Optional additional details"` } func main() { - s := server.NewMCPServer( - "Gotify Notification Server", - "1.0.0", - server.WithToolCapabilities(false), - ) + server := mcp.NewServer(&mcp.Implementation{ + Name: "Gotify Notification Server", + Version: "1.0.0", + }, nil) - sendMessageTool := mcp.NewTool("send-message", - mcp.WithDescription("Send a message to a Gotify server for notifications"), - mcp.WithString("message", - mcp.Required(), - mcp.Description("The message content to send"), - ), - mcp.WithString("title", - mcp.Description("Optional title for the message"), - ), - mcp.WithNumber("priority", - mcp.Description("Message priority (0-10, default: 5)"), - ), - ) + mcp.AddTool(server, &mcp.Tool{ + Name: "send-message", + Description: "Send a message to a Gotify server for notifications", + }, sendMessage) - askForHelpTool := mcp.NewTool("ask-for-help", - mcp.WithDescription("Send a help request notification to the user via Gotify"), - mcp.WithString("context", - mcp.Required(), - mcp.Description("Context or description of what help is needed"), - ), - mcp.WithString("error", - mcp.Description("Optional error message or details"), - ), - ) + mcp.AddTool(server, &mcp.Tool{ + Name: "ask-for-help", + Description: "Send a help request notification to the user via Gotify", + }, askForHelp) - notifyCompletionTool := mcp.NewTool("notify-completion", - mcp.WithDescription("Send a completion notification to the user via Gotify"), - mcp.WithString("task", - mcp.Required(), - mcp.Description("Description of the completed task"), - ), - mcp.WithString("result", - mcp.Description("Optional result or outcome details"), - ), - ) + mcp.AddTool(server, &mcp.Tool{ + Name: "notify-completion", + Description: "Send a completion notification to the user via Gotify", + }, notifyCompletion) - summarizeTool := mcp.NewTool("summarize-activity", - mcp.WithDescription("Send a summary of current activities or status to the user via Gotify"), - mcp.WithString("summary", - mcp.Required(), - mcp.Description("Summary of activities or current status"), - ), - mcp.WithString("details", - mcp.Description("Optional additional details"), - ), - ) + mcp.AddTool(server, &mcp.Tool{ + Name: "summarize-activity", + Description: "Send a summary of current activities or status to the user via Gotify", + }, summarizeActivity) - s.AddTool(sendMessageTool, sendMessage) - s.AddTool(askForHelpTool, askForHelp) - s.AddTool(notifyCompletionTool, notifyCompletion) - s.AddTool(summarizeTool, summarizeActivity) - - if err := server.ServeStdio(s); err != nil { - fmt.Printf("Server error: %v\n", err) + if err := server.Run(context.Background(), &mcp.StdioTransport{}); err != nil { + log.Printf("Server error: %v\n", err) } } @@ -126,49 +100,38 @@ func sendGotifyMessage(message GotifyMessage) error { return nil } -func sendMessage(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { - message, err := request.RequireString("message") - if err != nil { - return mcp.NewToolResultError(err.Error()), nil +func sendMessage(ctx context.Context, req *mcp.CallToolRequest, args SendMessageArgs) (*mcp.CallToolResult, any, error) { + priority := 5 + if args.Priority > 0 { + priority = int(args.Priority) } - args, ok := request.Params.Arguments.(map[string]any) - if !ok { - return mcp.NewToolResultError("invalid arguments type"), nil - } - - title := getStringArg(args, "title", "") - priority := getNumberArg(args, "priority", 5) - gotifyMsg := GotifyMessage{ - Message: message, - Title: title, - Priority: int(priority), + Message: args.Message, + Title: args.Title, + Priority: priority, } if err := sendGotifyMessage(gotifyMsg); err != nil { - return mcp.NewToolResultError(fmt.Sprintf("Failed to send message: %s", err)), nil + return &mcp.CallToolResult{ + Content: []mcp.Content{ + &mcp.TextContent{Text: fmt.Sprintf("Failed to send message: %s", err)}, + }, + IsError: true, + }, nil, nil } - return mcp.NewToolResultText("Message sent successfully"), nil + return &mcp.CallToolResult{ + Content: []mcp.Content{ + &mcp.TextContent{Text: "Message sent successfully"}, + }, + }, nil, nil } -func askForHelp(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { - contextStr, err := request.RequireString("context") - if err != nil { - return mcp.NewToolResultError(err.Error()), nil - } - - args, ok := request.Params.Arguments.(map[string]any) - if !ok { - return mcp.NewToolResultError("invalid arguments type"), nil - } - - errorMsg := getStringArg(args, "error", "") - - message := fmt.Sprintf("🆘 Help needed: %s", contextStr) - if errorMsg != "" { - message += fmt.Sprintf("\nError: %s", errorMsg) +func askForHelp(ctx context.Context, req *mcp.CallToolRequest, args AskForHelpArgs) (*mcp.CallToolResult, any, error) { + message := fmt.Sprintf("🆘 Help needed: %s", args.Context) + if args.Error != "" { + message += fmt.Sprintf("\nError: %s", args.Error) } gotifyMsg := GotifyMessage{ @@ -178,28 +141,25 @@ func askForHelp(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallTool } if err := sendGotifyMessage(gotifyMsg); err != nil { - return mcp.NewToolResultError(fmt.Sprintf("Failed to send help request: %s", err)), nil + return &mcp.CallToolResult{ + Content: []mcp.Content{ + &mcp.TextContent{Text: fmt.Sprintf("Failed to send help request: %s", err)}, + }, + IsError: true, + }, nil, nil } - return mcp.NewToolResultText("Help request sent successfully"), nil + return &mcp.CallToolResult{ + Content: []mcp.Content{ + &mcp.TextContent{Text: "Help request sent successfully"}, + }, + }, nil, nil } -func notifyCompletion(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { - task, err := request.RequireString("task") - if err != nil { - return mcp.NewToolResultError(err.Error()), nil - } - - args, ok := request.Params.Arguments.(map[string]any) - if !ok { - return mcp.NewToolResultError("invalid arguments type"), nil - } - - result := getStringArg(args, "result", "") - - message := fmt.Sprintf("✅ Task completed: %s", task) - if result != "" { - message += fmt.Sprintf("\nResult: %s", result) +func notifyCompletion(ctx context.Context, req *mcp.CallToolRequest, args NotifyCompletionArgs) (*mcp.CallToolResult, any, error) { + message := fmt.Sprintf("✅ Task completed: %s", args.Task) + if args.Result != "" { + message += fmt.Sprintf("\nResult: %s", args.Result) } gotifyMsg := GotifyMessage{ @@ -209,28 +169,25 @@ func notifyCompletion(ctx context.Context, request mcp.CallToolRequest) (*mcp.Ca } if err := sendGotifyMessage(gotifyMsg); err != nil { - return mcp.NewToolResultError(fmt.Sprintf("Failed to send completion notification: %s", err)), nil + return &mcp.CallToolResult{ + Content: []mcp.Content{ + &mcp.TextContent{Text: fmt.Sprintf("Failed to send completion notification: %s", err)}, + }, + IsError: true, + }, nil, nil } - return mcp.NewToolResultText("Completion notification sent successfully"), nil + return &mcp.CallToolResult{ + Content: []mcp.Content{ + &mcp.TextContent{Text: "Completion notification sent successfully"}, + }, + }, nil, nil } -func summarizeActivity(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { - summary, err := request.RequireString("summary") - if err != nil { - return mcp.NewToolResultError(err.Error()), nil - } - - args, ok := request.Params.Arguments.(map[string]any) - if !ok { - return mcp.NewToolResultError("invalid arguments type"), nil - } - - details := getStringArg(args, "details", "") - - message := fmt.Sprintf("📊 Activity Summary: %s", summary) - if details != "" { - message += fmt.Sprintf("\nDetails: %s", details) +func summarizeActivity(ctx context.Context, req *mcp.CallToolRequest, args SummarizeActivityArgs) (*mcp.CallToolResult, any, error) { + message := fmt.Sprintf("📊 Activity Summary: %s", args.Summary) + if args.Details != "" { + message += fmt.Sprintf("\nDetails: %s", args.Details) } gotifyMsg := GotifyMessage{ @@ -240,9 +197,17 @@ func summarizeActivity(ctx context.Context, request mcp.CallToolRequest) (*mcp.C } if err := sendGotifyMessage(gotifyMsg); err != nil { - return mcp.NewToolResultError(fmt.Sprintf("Failed to send summary: %s", err)), nil + return &mcp.CallToolResult{ + Content: []mcp.Content{ + &mcp.TextContent{Text: fmt.Sprintf("Failed to send summary: %s", err)}, + }, + IsError: true, + }, nil, nil } - return mcp.NewToolResultText("Activity summary sent successfully"), nil + return &mcp.CallToolResult{ + Content: []mcp.Content{ + &mcp.TextContent{Text: "Activity summary sent successfully"}, + }, + }, nil, nil } -