Files
Karti Tripathi a4e51f6412 feat: initial release with 48 Mattermost MCP tools
- 27 base tools: channels, messaging, users, reactions, files, DMs
- Team management: invite/remove users, get stats, list members
- Slash commands: execute /remind, /poll, etc.
- Webhook management: incoming and outgoing webhooks
- System tools: server config, logs, bulk status updates
- Channel admin: create, invite, leave, delete channels
- Read-only mode for safe exploration
- Dual token support (bot + PAT) for enhanced security
- Apache 2.0 licensed
2026-04-15 21:15:26 -07:00

182 lines
4.8 KiB
Go

package reaction
import (
"context"
"fmt"
"github.com/karti-ai/mattermost-mcp-server/pkg/log"
"github.com/karti-ai/mattermost-mcp-server/pkg/mattermost"
"github.com/karti-ai/mattermost-mcp-server/pkg/params"
"github.com/karti-ai/mattermost-mcp-server/pkg/to"
"github.com/karti-ai/mattermost-mcp-server/pkg/tool"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
"github.com/mattermost/mattermost-server/v6/model"
)
var Tool = tool.New()
const (
AddReactionToolName = "mattermost_add_reaction"
RemoveReactionToolName = "mattermost_remove_reaction"
ListReactionsToolName = "mattermost_list_reactions"
)
var (
AddReactionTool = mcp.NewTool(
AddReactionToolName,
mcp.WithDescription("Add emoji reaction to post"),
mcp.WithString("post_id", mcp.Required(), mcp.Description("Post ID to add reaction to")),
mcp.WithString("emoji_name", mcp.Required(), mcp.Description("Emoji name without colons (e.g., thumbsup, not :thumbsup:)")),
)
RemoveReactionTool = mcp.NewTool(
RemoveReactionToolName,
mcp.WithDescription("Remove emoji reaction from post"),
mcp.WithString("post_id", mcp.Required(), mcp.Description("Post ID to remove reaction from")),
mcp.WithString("emoji_name", mcp.Required(), mcp.Description("Emoji name without colons")),
)
ListReactionsTool = mcp.NewTool(
ListReactionsToolName,
mcp.WithDescription("List all emoji reactions on a post"),
mcp.WithString("post_id", mcp.Required(), mcp.Description("Post ID to get reactions for")),
)
)
func init() {
registerTools()
}
func registerTools() {
tools := []server.ServerTool{
{Tool: AddReactionTool, Handler: AddReactionFn},
{Tool: RemoveReactionTool, Handler: RemoveReactionFn},
{Tool: ListReactionsTool, Handler: ListReactionsFn},
}
for _, t := range tools {
if t.Tool.Name == ListReactionsToolName {
Tool.RegisterRead(t)
} else {
Tool.RegisterWrite(t)
}
}
}
func AddReactionFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("[Reaction] Called AddReactionFn")
args := req.GetArguments()
postId, err := params.GetString(args, "post_id")
if err != nil {
return to.Error(fmt.Errorf("[post_id] %v", err)), nil
}
emojiName, err := params.GetString(args, "emoji_name")
if err != nil {
return to.Error(fmt.Errorf("[emoji_name] %v", err)), nil
}
client := mattermost.GetGlobalClient()
if client == nil {
return to.Error(fmt.Errorf("[internal] client not initialized")), nil
}
// Get current user ID
me, err := client.GetMe(ctx)
if err != nil {
return to.Error(fmt.Errorf("[user] failed to get current user: %v", err)), nil
}
reaction := &model.Reaction{
UserId: me.Id,
PostId: postId,
EmojiName: emojiName,
}
result, err := client.SaveReaction(ctx, reaction)
if err != nil {
return to.Error(fmt.Errorf("[reaction] failed to add reaction: %v", err)), nil
}
return to.Result(SlimReaction(result)), nil
}
func RemoveReactionFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("[Reaction] Called RemoveReactionFn")
args := req.GetArguments()
postId, err := params.GetString(args, "post_id")
if err != nil {
return to.Error(fmt.Errorf("[post_id] %v", err)), nil
}
emojiName, err := params.GetString(args, "emoji_name")
if err != nil {
return to.Error(fmt.Errorf("[emoji_name] %v", err)), nil
}
client := mattermost.GetGlobalClient()
if client == nil {
return to.Error(fmt.Errorf("[internal] client not initialized")), nil
}
// Get current user ID
me, err := client.GetMe(ctx)
if err != nil {
return to.Error(fmt.Errorf("[user] failed to get current user: %v", err)), nil
}
reaction := &model.Reaction{
UserId: me.Id,
PostId: postId,
EmojiName: emojiName,
}
err = client.DeleteReaction(ctx, reaction)
if err != nil {
return to.Error(fmt.Errorf("[reaction] failed to remove reaction: %v", err)), nil
}
return to.Result(map[string]interface{}{
"success": true,
"post_id": postId,
"emoji_name": emojiName,
"message": "Reaction removed successfully",
}), nil
}
func ListReactionsFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
log.Debugf("[Reaction] Called ListReactionsFn")
args := req.GetArguments()
postId, err := params.GetString(args, "post_id")
if err != nil {
return to.Error(fmt.Errorf("[post_id] %v", err)), nil
}
client := mattermost.GetGlobalClient()
if client == nil {
return to.Error(fmt.Errorf("[internal] client not initialized")), nil
}
reactions, err := client.GetReactions(ctx, postId)
if err != nil {
return to.Error(fmt.Errorf("[reaction] failed to list reactions: %v", err)), nil
}
results := make([]map[string]interface{}, 0, len(reactions))
for _, r := range reactions {
results = append(results, SlimReaction(r))
}
return to.Result(map[string]interface{}{
"reactions": results,
"count": len(results),
"post_id": postId,
}), nil
}