35a0b2b715
- 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
221 lines
6.5 KiB
Go
221 lines
6.5 KiB
Go
package channel
|
|
|
|
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 AdminTool = tool.New()
|
|
|
|
const (
|
|
CreateChannelToolName = "mattermost_create_channel"
|
|
InviteToChannelToolName = "mattermost_invite_to_channel"
|
|
DeleteChannelToolName = "mattermost_delete_channel"
|
|
LeaveChannelToolName = "mattermost_leave_channel"
|
|
)
|
|
|
|
var (
|
|
CreateChannelTool = mcp.NewTool(
|
|
CreateChannelToolName,
|
|
mcp.WithDescription("Create a new channel (public or private) in a team"),
|
|
mcp.WithString("team_id", mcp.Required(), mcp.Description("Team to create channel in")),
|
|
mcp.WithString("name", mcp.Required(), mcp.Description("Channel name (lowercase, no spaces, 2-64 characters)")),
|
|
mcp.WithString("display_name", mcp.Required(), mcp.Description("Display name for the channel (2-64 characters)")),
|
|
mcp.WithString("type", mcp.Required(), mcp.Description("Channel type: 'O' for public, 'P' for private")),
|
|
mcp.WithString("purpose", mcp.Description("Channel description/purpose (optional)")),
|
|
)
|
|
|
|
InviteToChannelTool = mcp.NewTool(
|
|
InviteToChannelToolName,
|
|
mcp.WithDescription("Invite a user to a channel"),
|
|
mcp.WithString("channel_id", mcp.Required(), mcp.Description("Channel to invite user to")),
|
|
mcp.WithString("user_id", mcp.Required(), mcp.Description("User to invite")),
|
|
)
|
|
|
|
DeleteChannelTool = mcp.NewTool(
|
|
DeleteChannelToolName,
|
|
mcp.WithDescription("Delete/archive a channel (soft delete by default)"),
|
|
mcp.WithString("channel_id", mcp.Required(), mcp.Description("Channel to delete")),
|
|
mcp.WithBoolean("permanent", mcp.Description("Permanently delete instead of archive (default: false)")),
|
|
)
|
|
|
|
LeaveChannelTool = mcp.NewTool(
|
|
LeaveChannelToolName,
|
|
mcp.WithDescription("Remove self from a channel"),
|
|
mcp.WithString("channel_id", mcp.Required(), mcp.Description("Channel to leave")),
|
|
)
|
|
)
|
|
|
|
func init() {
|
|
registerAdminTools()
|
|
}
|
|
|
|
func registerAdminTools() {
|
|
tools := []server.ServerTool{
|
|
{Tool: CreateChannelTool, Handler: CreateChannelFn},
|
|
{Tool: InviteToChannelTool, Handler: InviteToChannelFn},
|
|
{Tool: DeleteChannelTool, Handler: DeleteChannelFn},
|
|
{Tool: LeaveChannelTool, Handler: LeaveChannelFn},
|
|
}
|
|
for _, t := range tools {
|
|
AdminTool.RegisterWrite(t)
|
|
}
|
|
}
|
|
|
|
func CreateChannelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
log.Debugf("[Channel] Called CreateChannelFn")
|
|
|
|
args := req.GetArguments()
|
|
|
|
teamID, err := params.GetString(args, "team_id")
|
|
if err != nil {
|
|
return to.Error(fmt.Errorf("[team_id] %v", err)), nil
|
|
}
|
|
|
|
name, err := params.GetString(args, "name")
|
|
if err != nil {
|
|
return to.Error(fmt.Errorf("[name] %v", err)), nil
|
|
}
|
|
|
|
displayName, err := params.GetString(args, "display_name")
|
|
if err != nil {
|
|
return to.Error(fmt.Errorf("[display_name] %v", err)), nil
|
|
}
|
|
|
|
channelType, err := params.GetString(args, "type")
|
|
if err != nil {
|
|
return to.Error(fmt.Errorf("[type] %v", err)), nil
|
|
}
|
|
|
|
if channelType != "O" && channelType != "P" {
|
|
return to.Error(fmt.Errorf("[type] must be 'O' (public) or 'P' (private)")), nil
|
|
}
|
|
|
|
purpose := params.GetOptionalString(args, "purpose", "")
|
|
|
|
client := mattermost.GetGlobalClient()
|
|
if client == nil {
|
|
return to.Error(fmt.Errorf("[internal] client not initialized")), nil
|
|
}
|
|
|
|
channel := &model.Channel{
|
|
TeamId: teamID,
|
|
Name: name,
|
|
DisplayName: displayName,
|
|
Type: model.ChannelType(channelType),
|
|
Purpose: purpose,
|
|
}
|
|
|
|
created, err := client.CreateChannel(ctx, channel)
|
|
if err != nil {
|
|
return to.Error(fmt.Errorf("[channel] failed to create channel: %v", err)), nil
|
|
}
|
|
|
|
return to.Result(SlimChannel(created)), nil
|
|
}
|
|
|
|
func InviteToChannelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
log.Debugf("[Channel] Called InviteToChannelFn")
|
|
|
|
args := req.GetArguments()
|
|
|
|
channelID, err := params.GetString(args, "channel_id")
|
|
if err != nil {
|
|
return to.Error(fmt.Errorf("[channel_id] %v", err)), nil
|
|
}
|
|
|
|
userID, err := params.GetString(args, "user_id")
|
|
if err != nil {
|
|
return to.Error(fmt.Errorf("[user_id] %v", err)), nil
|
|
}
|
|
|
|
client := mattermost.GetGlobalClient()
|
|
if client == nil {
|
|
return to.Error(fmt.Errorf("[internal] client not initialized")), nil
|
|
}
|
|
|
|
member, err := client.AddChannelMember(ctx, channelID, userID)
|
|
if err != nil {
|
|
return to.Error(fmt.Errorf("[channel] failed to invite user to channel: %v", err)), nil
|
|
}
|
|
|
|
return to.Result(map[string]interface{}{
|
|
"success": true,
|
|
"channel_id": channelID,
|
|
"user_id": userID,
|
|
"member": member,
|
|
"message": "User invited to channel successfully",
|
|
}), nil
|
|
}
|
|
|
|
func DeleteChannelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
log.Debugf("[Channel] Called DeleteChannelFn")
|
|
|
|
args := req.GetArguments()
|
|
|
|
channelID, err := params.GetString(args, "channel_id")
|
|
if err != nil {
|
|
return to.Error(fmt.Errorf("[channel_id] %v", err)), nil
|
|
}
|
|
|
|
_ = params.GetOptionalBool(args, "permanent", false)
|
|
|
|
client := mattermost.GetGlobalClient()
|
|
if client == nil {
|
|
return to.Error(fmt.Errorf("[internal] client not initialized")), nil
|
|
}
|
|
|
|
err = client.DeleteChannel(ctx, channelID)
|
|
if err != nil {
|
|
return to.Error(fmt.Errorf("[channel] failed to delete channel: %v", err)), nil
|
|
}
|
|
|
|
return to.Result(map[string]interface{}{
|
|
"success": true,
|
|
"channel_id": channelID,
|
|
"message": "Channel deleted successfully",
|
|
}), nil
|
|
}
|
|
|
|
func LeaveChannelFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
log.Debugf("[Channel] Called LeaveChannelFn")
|
|
|
|
args := req.GetArguments()
|
|
|
|
channelID, err := params.GetString(args, "channel_id")
|
|
if err != nil {
|
|
return to.Error(fmt.Errorf("[channel_id] %v", err)), nil
|
|
}
|
|
|
|
client := mattermost.GetGlobalClient()
|
|
if client == nil {
|
|
return to.Error(fmt.Errorf("[internal] client not initialized")), nil
|
|
}
|
|
|
|
user, err := client.GetMe(ctx)
|
|
if err != nil {
|
|
return to.Error(fmt.Errorf("[user] failed to get current user: %v", err)), nil
|
|
}
|
|
|
|
err = client.RemoveChannelMember(ctx, channelID, user.Id)
|
|
if err != nil {
|
|
return to.Error(fmt.Errorf("[channel] failed to leave channel: %v", err)), nil
|
|
}
|
|
|
|
return to.Result(map[string]interface{}{
|
|
"success": true,
|
|
"channel_id": channelID,
|
|
"user_id": user.Id,
|
|
"message": "Successfully left the channel",
|
|
}), nil
|
|
}
|