mirror of
https://github.com/taigrr/gotify-mcp.git
synced 2025-10-23 01:27:18 +03:00
247 lines
6.5 KiB
Go
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
|
|
} |