Files
gotify-mcp/main.go
Tai Groot 4151e02f74 initial
2025-07-22 03:15:18 -07:00

247 lines
6.5 KiB
Go

package main
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"os"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
type GotifyMessage struct {
Message string `json:"message"`
Title string `json:"title,omitempty"`
Priority int `json:"priority,omitempty"`
}
func getStringArg(args map[string]interface{}, key string, defaultValue string) string {
if val, ok := args[key].(string); ok {
return val
}
return defaultValue
}
func getNumberArg(args map[string]interface{}, key string, defaultValue float64) float64 {
if val, ok := args[key].(float64); ok {
return val
}
return defaultValue
}
func main() {
s := server.NewMCPServer(
"Gotify Notification Server",
"1.0.0",
server.WithToolCapabilities(false),
)
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)"),
),
)
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"),
),
)
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"),
),
)
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"),
),
)
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)
}
}
func sendGotifyMessage(message GotifyMessage) error {
gotifyURL := os.Getenv("GOTIFY_URL")
gotifyToken := os.Getenv("GOTIFY_TOKEN")
if gotifyURL == "" {
return fmt.Errorf("GOTIFY_URL environment variable is not set")
}
if gotifyToken == "" {
return fmt.Errorf("GOTIFY_TOKEN environment variable is not set")
}
jsonData, err := json.Marshal(message)
if err != nil {
return fmt.Errorf("failed to marshal message: %w", err)
}
url := fmt.Sprintf("%s/message?token=%s", gotifyURL, gotifyToken)
resp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonData))
if err != nil {
return fmt.Errorf("failed to send message: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("gotify server returned status: %d", resp.StatusCode)
}
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
}
args, ok := request.Params.Arguments.(map[string]interface{})
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),
}
if err := sendGotifyMessage(gotifyMsg); err != nil {
return mcp.NewToolResultError(fmt.Sprintf("Failed to send message: %s", err)), nil
}
return mcp.NewToolResultText("Message sent successfully"), 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]interface{})
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)
}
gotifyMsg := GotifyMessage{
Message: message,
Title: "Help Request",
Priority: 8,
}
if err := sendGotifyMessage(gotifyMsg); err != nil {
return mcp.NewToolResultError(fmt.Sprintf("Failed to send help request: %s", err)), nil
}
return mcp.NewToolResultText("Help request sent successfully"), 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]interface{})
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)
}
gotifyMsg := GotifyMessage{
Message: message,
Title: "Task Completed",
Priority: 6,
}
if err := sendGotifyMessage(gotifyMsg); err != nil {
return mcp.NewToolResultError(fmt.Sprintf("Failed to send completion notification: %s", err)), nil
}
return mcp.NewToolResultText("Completion notification sent successfully"), 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]interface{})
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)
}
gotifyMsg := GotifyMessage{
Message: message,
Title: "Activity Summary",
Priority: 4,
}
if err := sendGotifyMessage(gotifyMsg); err != nil {
return mcp.NewToolResultError(fmt.Sprintf("Failed to send summary: %s", err)), nil
}
return mcp.NewToolResultText("Activity summary sent successfully"), nil
}