mcp-dingdingbot-server

package main import ( "context" "fmt" "log" "os" "strings" "github.com/mark3labs/mcp-go/mcp" "github.com/mark3labs/mcp-go/server" ) func main() { webhookKey := os.Getenv("DINGDING_BOT_WEBHOOK_KEY") if webhookKey == "" { log.Println("DINGDING_BOT_WEBHOOK_KEY environment variable is required") return } // Get the sign key for signature verification (optional) signKey := os.Getenv("DINGDING_BOT_SIGN_KEY") bot := NewDingDingBot(DINGDING_BOT_SEND_URL, webhookKey, signKey) s := server.NewMCPServer( "mcp-dingdingbot-server", "1.0.0", server.WithResourceCapabilities(true, true), server.WithLogging(), ) sendTextTool := mcp.NewTool("send_text", mcp.WithDescription("Send a text message to DingDing group"), mcp.WithString("content", mcp.Required(), mcp.Description("Text content to send"), ), mcp.WithString("at_mobiles", mcp.Description("List of mobile numbers to mention, multiple numbers use commas to separate, such as 13800138000,13800138001"), ), mcp.WithString("at_user_ids", mcp.Description("List of user IDs to mention, multiple IDs use commas to separate"), ), mcp.WithBoolean("is_at_all", mcp.Description("Whether to mention all users in the group"), ), ) s.AddTool(sendTextTool, sendTextHandler(bot)) sendMarkdownTool := mcp.NewTool("send_markdown", mcp.WithDescription("Send a markdown message to DingDing group"), mcp.WithString("title", mcp.Required(), mcp.Description("Title of the markdown message"), ), mcp.WithString("content", mcp.Required(), mcp.Description("Markdown content to send"), ), mcp.WithString("at_mobiles", mcp.Description("List of mobile numbers to mention, multiple numbers use commas to separate"), ), mcp.WithString("at_user_ids", mcp.Description("List of user IDs to mention, multiple IDs use commas to separate"), ), mcp.WithBoolean("is_at_all", mcp.Description("Whether to mention all users in the group"), ), ) s.AddTool(sendMarkdownTool, sendMarkdownHandler(bot)) sendImageTool := mcp.NewTool("send_image", mcp.WithDescription("Send an image message to DingDing group"), mcp.WithString("base64_data", mcp.Required(), mcp.Description("Base64 encoded image data"), ), mcp.WithString("md5", mcp.Required(), mcp.Description("MD5 hash of the image"), ), ) s.AddTool(sendImageTool, sendImageHandler(bot)) sendNewsTool := mcp.NewTool("send_news", mcp.WithDescription("Send a link message to DingDing group"), mcp.WithString("title", mcp.Required(), mcp.Description("Title of the link message")), mcp.WithString("text", mcp.Required(), mcp.Description("Text content of the link message")), mcp.WithString("message_url", mcp.Required(), mcp.Description("URL of the link message")), mcp.WithString("pic_url", mcp.Description("Picture URL of the link message")), ) s.AddTool(sendNewsTool, sendNewsHandler(bot)) sendTemplateCardTool := mcp.NewTool("send_template_card", mcp.WithDescription("Send an action card message to DingDing group"), mcp.WithString("title", mcp.Required(), mcp.Description("Title of the action card"), ), mcp.WithString("text", mcp.Required(), mcp.Description("Text content of the action card"), ), mcp.WithString("single_title", mcp.Required(), mcp.Description("Title of the single button"), ), mcp.WithString("single_url", mcp.Required(), mcp.Description("URL for the single button"), ), mcp.WithString("btn_orientation", mcp.Description("Button orientation, 0: vertical, 1: horizontal"), ), ) s.AddTool(sendTemplateCardTool, sendTemplateCardHandler(bot)) uploadFileTool := mcp.NewTool("upload_file", mcp.WithDescription("Upload a file to DingDing"), mcp.WithString("file_path", mcp.Required(), mcp.Description("Path to the file to upload"), ), ) s.AddTool(uploadFileTool, uploadFileHandler(bot)) if err := server.ServeStdio(s); err != nil { log.Printf("Server error: %v\n", err) } } func sendTextHandler(bot *DingDingBot) func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { return func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { var atMobilesStr string var atUserIdsStr string var atMobiles []string var atUserIds []string var isAtAll bool content := request.Params.Arguments["content"].(string) if request.Params.Arguments["at_mobiles"] != nil { atMobilesStr = request.Params.Arguments["at_mobiles"].(string) atMobiles = strings.Split(atMobilesStr, ",") } else { atMobiles = []string{} } if request.Params.Arguments["at_user_ids"] != nil { atUserIdsStr = request.Params.Arguments["at_user_ids"].(string) atUserIds = strings.Split(atUserIdsStr, ",") } else { atUserIds = []string{} } if request.Params.Arguments["is_at_all"] != nil { isAtAll = request.Params.Arguments["is_at_all"].(bool) } else { isAtAll = false } err := bot.SendText(content, atMobiles, atUserIds, isAtAll) if err != nil { return mcp.NewToolResultError(fmt.Sprintf("Failed to send text message: %v", err)), nil } return mcp.NewToolResultText("Text message sent successfully"), nil } } func sendMarkdownHandler(bot *DingDingBot) func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { return func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { title := request.Params.Arguments["title"].(string) content := request.Params.Arguments["content"].(string) var atMobiles []string var atUserIds []string var isAtAll bool if request.Params.Arguments["at_mobiles"] != nil { atMobilesStr := request.Params.Arguments["at_mobiles"].(string) atMobiles = strings.Split(atMobilesStr, ",") } else { atMobiles = []string{} } if request.Params.Arguments["at_user_ids"] != nil { atUserIdsStr := request.Params.Arguments["at_user_ids"].(string) atUserIds = strings.Split(atUserIdsStr, ",") } else { atUserIds = []string{} } if request.Params.Arguments["is_at_all"] != nil { isAtAll = request.Params.Arguments["is_at_all"].(bool) } else { isAtAll = false } bot.WebhookURL = DINGDING_BOT_SEND_URL err := bot.SendMarkdown(title, content, atMobiles, atUserIds, isAtAll) if err != nil { return mcp.NewToolResultError(fmt.Sprintf("Failed to send markdown message: %v", err)), nil } return mcp.NewToolResultText("Markdown message sent successfully"), nil } } func sendImageHandler(bot *DingDingBot) func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { return func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { base64Data := request.Params.Arguments["base64_data"].(string) md5 := request.Params.Arguments["md5"].(string) bot.WebhookURL = DINGDING_BOT_SEND_URL err := bot.SendImage(base64Data, md5) if err != nil { return mcp.NewToolResultError(fmt.Sprintf("Failed to send image message: %v", err)), nil } return mcp.NewToolResultText("Image message sent successfully"), nil } } func sendNewsHandler(bot *DingDingBot) func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { return func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { // Extract the parameters from the request title := request.Params.Arguments["title"].(string) text := request.Params.Arguments["text"].(string) messageUrl := request.Params.Arguments["message_url"].(string) picUrl := "" if request.Params.Arguments["pic_url"] != nil { picUrl = request.Params.Arguments["pic_url"].(string) } // Send the news article bot.WebhookURL = DINGDING_BOT_SEND_URL err := bot.SendNews(title, text, messageUrl, picUrl) if err != nil { return mcp.NewToolResultError(fmt.Sprintf("Failed to send news message: %v", err)), nil } // Return success result return mcp.NewToolResultText("News message sent successfully"), nil } } func sendTemplateCardHandler(bot *DingDingBot) func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { return func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { title := request.Params.Arguments["title"].(string) text := request.Params.Arguments["text"].(string) singleTitle := request.Params.Arguments["single_title"].(string) singleURL := request.Params.Arguments["single_url"].(string) btnOrientation := "0" if request.Params.Arguments["btn_orientation"] != nil { btnOrientation = request.Params.Arguments["btn_orientation"].(string) } bot.WebhookURL = DINGDING_BOT_SEND_URL err := bot.SendTemplateCard(title, text, singleTitle, singleURL, btnOrientation) if err != nil { return mcp.NewToolResultError(fmt.Sprintf("Failed to send template card message: %v", err)), nil } return mcp.NewToolResultText("Template card message sent successfully"), nil } } func uploadFileHandler(bot *DingDingBot) func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { return func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { filePath := request.Params.Arguments["file_path"].(string) bot.WebhookURL = DINGDING_BOT_UPLOAD_URL mediaID, err := bot.UploadFile(filePath) if err != nil { return mcp.NewToolResultError(fmt.Sprintf("Failed to upload file: %v", err)), nil } return mcp.NewToolResultText(fmt.Sprintf("File uploaded successfully, media ID: %s", mediaID)), nil } }