update to use official libraries

This commit is contained in:
Tai Groot
2025-10-16 02:34:25 -04:00
parent 9cdd9fc8c4
commit 2d419b1e31
3 changed files with 115 additions and 167 deletions

7
go.mod
View File

@@ -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
)

32
go.sum
View File

@@ -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=

243
main.go
View File

@@ -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
}