Skip to main content
Glama
index.js88.1 kB
#!/usr/bin/env node "use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; Object.defineProperty(exports, "__esModule", { value: true }); var index_js_1 = require("@modelcontextprotocol/sdk/server/index.js"); var stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js"); var types_js_1 = require("@modelcontextprotocol/sdk/types.js"); var web_api_1 = require("@slack/web-api"); var zod_1 = require("zod"); var dotenv = require("dotenv"); var axios_1 = require("axios"); // Carregar variáveis de ambiente dotenv.config(); // Validação do schema para argumentos var SlackMessageSchema = zod_1.z.object({ channel: zod_1.z.string().describe('ID ou nome do canal Slack'), text: zod_1.z.string().describe('Texto da mensagem'), thread_ts: zod_1.z.string().optional().describe('Timestamp do thread (opcional)'), }); var SlackChannelSchema = zod_1.z.object({ types: zod_1.z.string().optional().describe('Tipos de canais: public_channel,private_channel,mpim,im'), limit: zod_1.z.number().optional().describe('Número máximo de canais (padrão: 100)'), }); var SlackUserSchema = zod_1.z.object({ limit: zod_1.z.number().optional().describe('Número máximo de usuários (padrão: 100)'), }); var SlackFileUploadSchema = zod_1.z.object({ channels: zod_1.z.string().describe('Canal para upload (ID ou nome)'), file_path: zod_1.z.string().describe('Caminho do arquivo local'), title: zod_1.z.string().optional().describe('Título do arquivo'), initial_comment: zod_1.z.string().optional().describe('Comentário inicial'), }); var SlackReactionSchema = zod_1.z.object({ channel: zod_1.z.string().describe('ID do canal'), timestamp: zod_1.z.string().describe('Timestamp da mensagem'), name: zod_1.z.string().describe('Nome do emoji (sem :)'), }); var SlackScheduleMessageSchema = zod_1.z.object({ channel: zod_1.z.string().describe('Canal de destino'), text: zod_1.z.string().describe('Texto da mensagem'), post_at: zod_1.z.number().describe('Timestamp Unix para envio'), }); var SlackThreadReplySchema = zod_1.z.object({ channel: zod_1.z.string().describe('ID do canal'), thread_ts: zod_1.z.string().describe('Timestamp do thread principal'), text: zod_1.z.string().describe('Texto da resposta'), }); var SlackUserStatusSchema = zod_1.z.object({ status_text: zod_1.z.string().describe('Texto do status'), status_emoji: zod_1.z.string().describe('Emoji do status (ex: :house:)'), status_expiration: zod_1.z.number().optional().describe('Timestamp de expiração'), }); var SlackPollSchema = zod_1.z.object({ channel: zod_1.z.string().describe('Canal para a enquete'), question: zod_1.z.string().describe('Pergunta da enquete'), options: zod_1.z.array(zod_1.z.string()).describe('Opções da enquete'), anonymous: zod_1.z.boolean().optional().describe('Enquete anônima'), }); var SlackWebhookSchema = zod_1.z.object({ url: zod_1.z.string().describe('URL do webhook'), text: zod_1.z.string().describe('Texto da mensagem'), username: zod_1.z.string().optional().describe('Nome do bot'), icon_emoji: zod_1.z.string().optional().describe('Emoji do ícone'), }); var SlackAnalyticsSchema = zod_1.z.object({ channel: zod_1.z.string().describe('Canal para análise'), days: zod_1.z.number().optional().describe('Dias para análise (padrão: 7)'), }); var SlackMentionSchema = zod_1.z.object({ channel: zod_1.z.string().describe('Canal da mensagem'), text: zod_1.z.string().describe('Texto da mensagem'), users: zod_1.z.array(zod_1.z.string()).describe('IDs dos usuários para mencionar'), }); var SlackMCPServer = /** @class */ (function () { function SlackMCPServer() { this.server = new index_js_1.Server({ name: 'slack-mcp-server', version: '1.0.0', }, { capabilities: { tools: {}, }, }); // Configurar cliente Slack var slackToken = process.env.SLACK_BOT_TOKEN; if (!slackToken) { throw new Error('SLACK_BOT_TOKEN não encontrado nas variáveis de ambiente'); } this.slackClient = new web_api_1.WebClient(slackToken); this.setupToolHandlers(); } SlackMCPServer.prototype.setupToolHandlers = function () { var _this = this; // Listar ferramentas disponíveis this.server.setRequestHandler(types_js_1.ListToolsRequestSchema, function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, { tools: [ { name: 'send_slack_message', description: 'Enviar uma mensagem para um canal do Slack', inputSchema: { type: 'object', properties: { channel: { type: 'string', description: 'ID ou nome do canal Slack (ex: #geral, C1234567890)', }, text: { type: 'string', description: 'Texto da mensagem a ser enviada', }, thread_ts: { type: 'string', description: 'Timestamp do thread para responder (opcional)', }, }, required: ['channel', 'text'], }, }, { name: 'list_slack_channels', description: 'Listar canais do Slack', inputSchema: { type: 'object', properties: { types: { type: 'string', description: 'Tipos de canais: public_channel,private_channel,mpim,im', default: 'public_channel,private_channel', }, limit: { type: 'number', description: 'Número máximo de canais', default: 100, }, }, required: [], }, }, { name: 'list_slack_users', description: 'Listar usuários do workspace Slack', inputSchema: { type: 'object', properties: { limit: { type: 'number', description: 'Número máximo de usuários', default: 100, }, }, required: [], }, }, { name: 'get_slack_channel_history', description: 'Obter histórico de mensagens de um canal', inputSchema: { type: 'object', properties: { channel: { type: 'string', description: 'ID do canal Slack', }, limit: { type: 'number', description: 'Número máximo de mensagens', default: 10, }, }, required: ['channel'], }, }, { name: 'upload_file_to_slack', description: 'Fazer upload de arquivo para canal Slack', inputSchema: { type: 'object', properties: { channels: { type: 'string', description: 'Canal para upload (ID ou nome)', }, file_path: { type: 'string', description: 'Caminho do arquivo local', }, title: { type: 'string', description: 'Título do arquivo', }, initial_comment: { type: 'string', description: 'Comentário inicial', }, }, required: ['channels', 'file_path'], }, }, { name: 'add_slack_reaction', description: 'Adicionar reação emoji a uma mensagem', inputSchema: { type: 'object', properties: { channel: { type: 'string', description: 'ID do canal', }, timestamp: { type: 'string', description: 'Timestamp da mensagem', }, name: { type: 'string', description: 'Nome do emoji (sem os :)', }, }, required: ['channel', 'timestamp', 'name'], }, }, { name: 'schedule_slack_message', description: 'Agendar mensagem para envio futuro', inputSchema: { type: 'object', properties: { channel: { type: 'string', description: 'Canal de destino', }, text: { type: 'string', description: 'Texto da mensagem', }, post_at: { type: 'number', description: 'Timestamp Unix para envio', }, }, required: ['channel', 'text', 'post_at'], }, }, { name: 'reply_in_thread', description: 'Responder em thread específico', inputSchema: { type: 'object', properties: { channel: { type: 'string', description: 'ID do canal', }, thread_ts: { type: 'string', description: 'Timestamp do thread principal', }, text: { type: 'string', description: 'Texto da resposta', }, }, required: ['channel', 'thread_ts', 'text'], }, }, { name: 'set_user_status', description: 'Definir status do usuário (presença)', inputSchema: { type: 'object', properties: { status_text: { type: 'string', description: 'Texto do status', }, status_emoji: { type: 'string', description: 'Emoji do status (ex: :house:)', }, status_expiration: { type: 'number', description: 'Timestamp de expiração', }, }, required: ['status_text', 'status_emoji'], }, }, { name: 'search_slack_messages', description: 'Buscar mensagens no workspace', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Termo de busca', }, count: { type: 'number', description: 'Número de resultados', default: 20, }, sort: { type: 'string', description: 'Ordenação: timestamp ou score', default: 'timestamp', }, }, required: ['query'], }, }, { name: 'get_workspace_info', description: 'Obter informações do workspace Slack', inputSchema: { type: 'object', properties: {}, required: [], }, }, { name: 'create_slack_reminder', description: 'Criar lembrete no Slack', inputSchema: { type: 'object', properties: { text: { type: 'string', description: 'Texto do lembrete', }, time: { type: 'string', description: 'Quando lembrar (ex: "in 5 minutes", "tomorrow at 9am")', }, user: { type: 'string', description: 'ID do usuário (opcional, padrão: próprio usuário)', }, }, required: ['text', 'time'], }, }, { name: 'create_slack_poll', description: 'Criar enquete interativa no Slack', inputSchema: { type: 'object', properties: { channel: { type: 'string', description: 'Canal para a enquete', }, question: { type: 'string', description: 'Pergunta da enquete', }, options: { type: 'array', items: { type: 'string' }, description: 'Opções da enquete (máximo 10)', }, anonymous: { type: 'boolean', description: 'Enquete anônima', default: false, }, }, required: ['channel', 'question', 'options'], }, }, { name: 'send_webhook_message', description: 'Enviar mensagem via webhook (para integrações)', inputSchema: { type: 'object', properties: { url: { type: 'string', description: 'URL do webhook Slack', }, text: { type: 'string', description: 'Texto da mensagem', }, username: { type: 'string', description: 'Nome do bot', }, icon_emoji: { type: 'string', description: 'Emoji do ícone (ex: :robot_face:)', }, }, required: ['url', 'text'], }, }, { name: 'get_channel_analytics', description: 'Obter analytics de um canal (atividade, engajamento)', inputSchema: { type: 'object', properties: { channel: { type: 'string', description: 'ID do canal', }, days: { type: 'number', description: 'Dias para análise', default: 7, }, }, required: ['channel'], }, }, { name: 'mention_users', description: 'Enviar mensagem mencionando usuários específicos', inputSchema: { type: 'object', properties: { channel: { type: 'string', description: 'Canal da mensagem', }, text: { type: 'string', description: 'Texto da mensagem', }, users: { type: 'array', items: { type: 'string' }, description: 'IDs dos usuários para mencionar', }, }, required: ['channel', 'text', 'users'], }, }, { name: 'archive_old_channels', description: 'Arquivar canais inativos automaticamente', inputSchema: { type: 'object', properties: { days_inactive: { type: 'number', description: 'Dias sem atividade para arquivar', default: 30, }, dry_run: { type: 'boolean', description: 'Apenas simular (não arquivar)', default: true, }, }, required: [], }, }, { name: 'bulk_invite_users', description: 'Convidar múltiplos usuários para canal', inputSchema: { type: 'object', properties: { channel: { type: 'string', description: 'ID do canal', }, users: { type: 'array', items: { type: 'string' }, description: 'IDs dos usuários', }, }, required: ['channel', 'users'], }, }, { name: 'get_private_channels_with_user', description: 'Listar grupos privados que o usuário participa', inputSchema: { type: 'object', properties: { user_id: { type: 'string', description: 'ID do usuário (opcional, padrão: bot atual)', }, include_history: { type: 'boolean', description: 'Incluir última mensagem de cada grupo', default: false, }, }, required: [], }, }, { name: 'search_in_private_groups', description: 'Buscar mensagens especificamente em grupos privados', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Termo de busca', }, group_ids: { type: 'array', items: { type: 'string' }, description: 'IDs dos grupos específicos (opcional)', }, count: { type: 'number', description: 'Número de resultados', default: 20, }, }, required: ['query'], }, }, ], }]; }); }); }); // Executar ferramentas this.server.setRequestHandler(types_js_1.CallToolRequestSchema, function (request) { return __awaiter(_this, void 0, void 0, function () { var _a, name, args, _b, error_1; return __generator(this, function (_c) { switch (_c.label) { case 0: _a = request.params, name = _a.name, args = _a.arguments; _c.label = 1; case 1: _c.trys.push([1, 44, , 45]); _b = name; switch (_b) { case 'send_slack_message': return [3 /*break*/, 2]; case 'list_slack_channels': return [3 /*break*/, 4]; case 'list_slack_users': return [3 /*break*/, 6]; case 'get_slack_channel_history': return [3 /*break*/, 8]; case 'upload_file_to_slack': return [3 /*break*/, 10]; case 'add_slack_reaction': return [3 /*break*/, 12]; case 'schedule_slack_message': return [3 /*break*/, 14]; case 'reply_in_thread': return [3 /*break*/, 16]; case 'set_user_status': return [3 /*break*/, 18]; case 'search_slack_messages': return [3 /*break*/, 20]; case 'get_workspace_info': return [3 /*break*/, 22]; case 'create_slack_reminder': return [3 /*break*/, 24]; case 'create_slack_poll': return [3 /*break*/, 26]; case 'send_webhook_message': return [3 /*break*/, 28]; case 'get_channel_analytics': return [3 /*break*/, 30]; case 'mention_users': return [3 /*break*/, 32]; case 'archive_old_channels': return [3 /*break*/, 34]; case 'bulk_invite_users': return [3 /*break*/, 36]; case 'get_private_channels_with_user': return [3 /*break*/, 38]; case 'search_in_private_groups': return [3 /*break*/, 40]; } return [3 /*break*/, 42]; case 2: return [4 /*yield*/, this.sendSlackMessage(args)]; case 3: return [2 /*return*/, _c.sent()]; case 4: return [4 /*yield*/, this.listSlackChannels(args)]; case 5: return [2 /*return*/, _c.sent()]; case 6: return [4 /*yield*/, this.listSlackUsers(args)]; case 7: return [2 /*return*/, _c.sent()]; case 8: return [4 /*yield*/, this.getChannelHistory(args)]; case 9: return [2 /*return*/, _c.sent()]; case 10: return [4 /*yield*/, this.uploadFile(args)]; case 11: return [2 /*return*/, _c.sent()]; case 12: return [4 /*yield*/, this.addReaction(args)]; case 13: return [2 /*return*/, _c.sent()]; case 14: return [4 /*yield*/, this.scheduleMessage(args)]; case 15: return [2 /*return*/, _c.sent()]; case 16: return [4 /*yield*/, this.replyInThread(args)]; case 17: return [2 /*return*/, _c.sent()]; case 18: return [4 /*yield*/, this.setUserStatus(args)]; case 19: return [2 /*return*/, _c.sent()]; case 20: return [4 /*yield*/, this.searchMessages(args)]; case 21: return [2 /*return*/, _c.sent()]; case 22: return [4 /*yield*/, this.getWorkspaceInfo()]; case 23: return [2 /*return*/, _c.sent()]; case 24: return [4 /*yield*/, this.createReminder(args)]; case 25: return [2 /*return*/, _c.sent()]; case 26: return [4 /*yield*/, this.createPoll(args)]; case 27: return [2 /*return*/, _c.sent()]; case 28: return [4 /*yield*/, this.sendWebhookMessage(args)]; case 29: return [2 /*return*/, _c.sent()]; case 30: return [4 /*yield*/, this.getChannelAnalytics(args)]; case 31: return [2 /*return*/, _c.sent()]; case 32: return [4 /*yield*/, this.mentionUsers(args)]; case 33: return [2 /*return*/, _c.sent()]; case 34: return [4 /*yield*/, this.archiveOldChannels(args)]; case 35: return [2 /*return*/, _c.sent()]; case 36: return [4 /*yield*/, this.bulkInviteUsers(args)]; case 37: return [2 /*return*/, _c.sent()]; case 38: return [4 /*yield*/, this.getPrivateChannelsWithUser(args)]; case 39: return [2 /*return*/, _c.sent()]; case 40: return [4 /*yield*/, this.searchInPrivateGroups(args)]; case 41: return [2 /*return*/, _c.sent()]; case 42: throw new types_js_1.McpError(types_js_1.ErrorCode.MethodNotFound, "Ferramenta desconhecida: ".concat(name)); case 43: return [3 /*break*/, 45]; case 44: error_1 = _c.sent(); if (error_1 instanceof types_js_1.McpError) { throw error_1; } throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, "Erro ao executar ".concat(name, ": ").concat(error_1 instanceof Error ? error_1.message : String(error_1))); case 45: return [2 /*return*/]; } }); }); }); }; SlackMCPServer.prototype.sendSlackMessage = function (args) { return __awaiter(this, void 0, void 0, function () { var _a, channel, text, thread_ts, result; return __generator(this, function (_b) { switch (_b.label) { case 0: _a = SlackMessageSchema.parse(args), channel = _a.channel, text = _a.text, thread_ts = _a.thread_ts; return [4 /*yield*/, this.slackClient.chat.postMessage({ channel: channel, text: text, thread_ts: thread_ts, })]; case 1: result = _b.sent(); return [2 /*return*/, { content: [ { type: 'text', text: JSON.stringify({ success: true, message: 'Mensagem enviada com sucesso', channel: result.channel, ts: result.ts, message_text: text, }, null, 2), }, ], }]; } }); }); }; SlackMCPServer.prototype.listSlackChannels = function (args) { return __awaiter(this, void 0, void 0, function () { var _a, _b, types, _c, limit, result, channels; var _d; return __generator(this, function (_e) { switch (_e.label) { case 0: _a = SlackChannelSchema.parse(args), _b = _a.types, types = _b === void 0 ? 'public_channel,private_channel' : _b, _c = _a.limit, limit = _c === void 0 ? 100 : _c; return [4 /*yield*/, this.slackClient.conversations.list({ types: types, limit: limit, })]; case 1: result = _e.sent(); channels = ((_d = result.channels) === null || _d === void 0 ? void 0 : _d.map(function (channel) { var _a, _b; return ({ id: channel.id, name: channel.name, is_private: channel.is_private, is_member: channel.is_member, topic: (_a = channel.topic) === null || _a === void 0 ? void 0 : _a.value, purpose: (_b = channel.purpose) === null || _b === void 0 ? void 0 : _b.value, num_members: channel.num_members, }); })) || []; return [2 /*return*/, { content: [ { type: 'text', text: JSON.stringify({ success: true, channels_count: channels.length, channels: channels, }, null, 2), }, ], }]; } }); }); }; SlackMCPServer.prototype.listSlackUsers = function (args) { return __awaiter(this, void 0, void 0, function () { var _a, limit, result, users; var _b; return __generator(this, function (_c) { switch (_c.label) { case 0: _a = SlackUserSchema.parse(args).limit, limit = _a === void 0 ? 100 : _a; return [4 /*yield*/, this.slackClient.users.list({ limit: limit, })]; case 1: result = _c.sent(); users = ((_b = result.members) === null || _b === void 0 ? void 0 : _b.map(function (user) { var _a, _b; return ({ id: user.id, name: user.name, real_name: user.real_name, display_name: (_a = user.profile) === null || _a === void 0 ? void 0 : _a.display_name, email: (_b = user.profile) === null || _b === void 0 ? void 0 : _b.email, is_bot: user.is_bot, is_admin: user.is_admin, is_owner: user.is_owner, }); })) || []; return [2 /*return*/, { content: [ { type: 'text', text: JSON.stringify({ success: true, users_count: users.length, users: users, }, null, 2), }, ], }]; } }); }); }; SlackMCPServer.prototype.getChannelHistory = function (args) { return __awaiter(this, void 0, void 0, function () { var channel, _a, limit, result, messages; var _b; return __generator(this, function (_c) { switch (_c.label) { case 0: channel = args.channel, _a = args.limit, limit = _a === void 0 ? 10 : _a; return [4 /*yield*/, this.slackClient.conversations.history({ channel: channel, limit: limit, })]; case 1: result = _c.sent(); messages = ((_b = result.messages) === null || _b === void 0 ? void 0 : _b.map(function (message) { return ({ ts: message.ts, user: message.user, text: message.text, thread_ts: message.thread_ts, reply_count: message.reply_count, }); })) || []; return [2 /*return*/, { content: [ { type: 'text', text: JSON.stringify({ success: true, channel: channel, messages_count: messages.length, messages: messages, }, null, 2), }, ], }]; } }); }); }; SlackMCPServer.prototype.uploadFile = function (args) { return __awaiter(this, void 0, void 0, function () { var _a, channels, file_path, title, initial_comment, fs, result, error_2; var _b, _c, _d; return __generator(this, function (_e) { switch (_e.label) { case 0: _a = SlackFileUploadSchema.parse(args), channels = _a.channels, file_path = _a.file_path, title = _a.title, initial_comment = _a.initial_comment; _e.label = 1; case 1: _e.trys.push([1, 4, , 5]); return [4 /*yield*/, Promise.resolve().then(function () { return require('fs'); })]; case 2: fs = _e.sent(); return [4 /*yield*/, this.slackClient.files.uploadV2({ channels: channels, file: fs.createReadStream(file_path), title: title, initial_comment: initial_comment, })]; case 3: result = _e.sent(); return [2 /*return*/, { content: [ { type: 'text', text: JSON.stringify({ success: true, message: 'Arquivo enviado com sucesso', file: { id: (_b = result.file) === null || _b === void 0 ? void 0 : _b.id, name: (_c = result.file) === null || _c === void 0 ? void 0 : _c.name, url: (_d = result.file) === null || _d === void 0 ? void 0 : _d.url_private, }, }, null, 2), }, ], }]; case 4: error_2 = _e.sent(); throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, "Erro ao fazer upload: ".concat(error_2 instanceof Error ? error_2.message : String(error_2))); case 5: return [2 /*return*/]; } }); }); }; SlackMCPServer.prototype.addReaction = function (args) { return __awaiter(this, void 0, void 0, function () { var _a, channel, timestamp, name, result; return __generator(this, function (_b) { switch (_b.label) { case 0: _a = SlackReactionSchema.parse(args), channel = _a.channel, timestamp = _a.timestamp, name = _a.name; return [4 /*yield*/, this.slackClient.reactions.add({ channel: channel, timestamp: timestamp, name: name, })]; case 1: result = _b.sent(); return [2 /*return*/, { content: [ { type: 'text', text: JSON.stringify({ success: true, message: "Rea\u00E7\u00E3o :".concat(name, ": adicionada com sucesso"), reaction: name, timestamp: timestamp, }, null, 2), }, ], }]; } }); }); }; SlackMCPServer.prototype.scheduleMessage = function (args) { return __awaiter(this, void 0, void 0, function () { var _a, channel, text, post_at, result; return __generator(this, function (_b) { switch (_b.label) { case 0: _a = SlackScheduleMessageSchema.parse(args), channel = _a.channel, text = _a.text, post_at = _a.post_at; return [4 /*yield*/, this.slackClient.chat.scheduleMessage({ channel: channel, text: text, post_at: post_at, })]; case 1: result = _b.sent(); return [2 /*return*/, { content: [ { type: 'text', text: JSON.stringify({ success: true, message: 'Mensagem agendada com sucesso', scheduled_message_id: result.scheduled_message_id, post_at: new Date(post_at * 1000).toISOString(), }, null, 2), }, ], }]; } }); }); }; SlackMCPServer.prototype.replyInThread = function (args) { return __awaiter(this, void 0, void 0, function () { var _a, channel, thread_ts, text, result; return __generator(this, function (_b) { switch (_b.label) { case 0: _a = SlackThreadReplySchema.parse(args), channel = _a.channel, thread_ts = _a.thread_ts, text = _a.text; return [4 /*yield*/, this.slackClient.chat.postMessage({ channel: channel, text: text, thread_ts: thread_ts, })]; case 1: result = _b.sent(); return [2 /*return*/, { content: [ { type: 'text', text: JSON.stringify({ success: true, message: 'Resposta enviada no thread', thread_ts: thread_ts, message_ts: result.ts, }, null, 2), }, ], }]; } }); }); }; SlackMCPServer.prototype.setUserStatus = function (args) { return __awaiter(this, void 0, void 0, function () { var _a, status_text, status_emoji, status_expiration, result; return __generator(this, function (_b) { switch (_b.label) { case 0: _a = SlackUserStatusSchema.parse(args), status_text = _a.status_text, status_emoji = _a.status_emoji, status_expiration = _a.status_expiration; return [4 /*yield*/, this.slackClient.users.profile.set({ profile: { status_text: status_text, status_emoji: status_emoji, status_expiration: status_expiration || 0, }, })]; case 1: result = _b.sent(); return [2 /*return*/, { content: [ { type: 'text', text: JSON.stringify({ success: true, message: 'Status do usuário atualizado', status: { text: status_text, emoji: status_emoji, expiration: status_expiration ? new Date(status_expiration * 1000).toISOString() : null, }, }, null, 2), }, ], }]; } }); }); }; SlackMCPServer.prototype.searchMessages = function (args) { return __awaiter(this, void 0, void 0, function () { var query, _a, count, _b, sort, result, matches; var _c, _d, _e; return __generator(this, function (_f) { switch (_f.label) { case 0: query = args.query, _a = args.count, count = _a === void 0 ? 20 : _a, _b = args.sort, sort = _b === void 0 ? 'timestamp' : _b; return [4 /*yield*/, this.slackClient.search.messages({ query: query, count: count, sort: sort, })]; case 1: result = _f.sent(); matches = ((_d = (_c = result.messages) === null || _c === void 0 ? void 0 : _c.matches) === null || _d === void 0 ? void 0 : _d.map(function (match) { var _a; return ({ text: match.text, user: match.username, channel: (_a = match.channel) === null || _a === void 0 ? void 0 : _a.name, ts: match.ts, permalink: match.permalink, }); })) || []; return [2 /*return*/, { content: [ { type: 'text', text: JSON.stringify({ success: true, query: query, total_matches: (_e = result.messages) === null || _e === void 0 ? void 0 : _e.total, matches_count: matches.length, matches: matches, }, null, 2), }, ], }]; } }); }); }; SlackMCPServer.prototype.getWorkspaceInfo = function () { return __awaiter(this, void 0, void 0, function () { var teamInfo, authTest; var _a, _b, _c, _d, _e; return __generator(this, function (_f) { switch (_f.label) { case 0: return [4 /*yield*/, this.slackClient.team.info()]; case 1: teamInfo = _f.sent(); return [4 /*yield*/, this.slackClient.auth.test()]; case 2: authTest = _f.sent(); return [2 /*return*/, { content: [ { type: 'text', text: JSON.stringify({ success: true, workspace: { id: (_a = teamInfo.team) === null || _a === void 0 ? void 0 : _a.id, name: (_b = teamInfo.team) === null || _b === void 0 ? void 0 : _b.name, domain: (_c = teamInfo.team) === null || _c === void 0 ? void 0 : _c.domain, email_domain: (_d = teamInfo.team) === null || _d === void 0 ? void 0 : _d.email_domain, icon: (_e = teamInfo.team) === null || _e === void 0 ? void 0 : _e.icon, }, bot: { user_id: authTest.user_id, bot_id: authTest.bot_id, team_id: authTest.team_id, }, }, null, 2), }, ], }]; } }); }); }; SlackMCPServer.prototype.createReminder = function (args) { return __awaiter(this, void 0, void 0, function () { var text, time, user, result; var _a, _b, _c, _d; return __generator(this, function (_e) { switch (_e.label) { case 0: text = args.text, time = args.time, user = args.user; return [4 /*yield*/, this.slackClient.reminders.add({ text: text, time: time, user: user, })]; case 1: result = _e.sent(); return [2 /*return*/, { content: [ { type: 'text', text: JSON.stringify({ success: true, message: 'Lembrete criado com sucesso', reminder: { id: (_a = result.reminder) === null || _a === void 0 ? void 0 : _a.id, text: (_b = result.reminder) === null || _b === void 0 ? void 0 : _b.text, time: (_c = result.reminder) === null || _c === void 0 ? void 0 : _c.time, user: (_d = result.reminder) === null || _d === void 0 ? void 0 : _d.user, }, }, null, 2), }, ], }]; } }); }); }; SlackMCPServer.prototype.createPoll = function (args) { return __awaiter(this, void 0, void 0, function () { var _a, channel, question, options, _b, anonymous, blocks, actions, result; return __generator(this, function (_c) { switch (_c.label) { case 0: _a = SlackPollSchema.parse(args), channel = _a.channel, question = _a.question, options = _a.options, _b = _a.anonymous, anonymous = _b === void 0 ? false : _b; blocks = [ { type: 'section', text: { type: 'mrkdwn', text: "*".concat(question, "*").concat(anonymous ? ' (Enquete Anônima)' : ''), }, }, { type: 'divider', }, ]; actions = options.slice(0, 10).map(function (option, index) { return ({ type: 'button', text: { type: 'plain_text', text: option, }, action_id: "poll_option_".concat(index), value: "".concat(index), }); }); blocks.push({ type: 'actions', elements: actions, }); return [4 /*yield*/, this.slackClient.chat.postMessage({ channel: channel, text: question, blocks: blocks, })]; case 1: result = _c.sent(); return [2 /*return*/, { content: [ { type: 'text', text: JSON.stringify({ success: true, message: 'Enquete criada com sucesso', poll: { question: question, options: options, message_ts: result.ts, anonymous: anonymous, }, }, null, 2), }, ], }]; } }); }); }; SlackMCPServer.prototype.sendWebhookMessage = function (args) { return __awaiter(this, void 0, void 0, function () { var _a, url, text, username, icon_emoji, payload, response, error_3; return __generator(this, function (_b) { switch (_b.label) { case 0: _a = SlackWebhookSchema.parse(args), url = _a.url, text = _a.text, username = _a.username, icon_emoji = _a.icon_emoji; _b.label = 1; case 1: _b.trys.push([1, 3, , 4]); payload = { text: text, username: username, icon_emoji: icon_emoji, }; return [4 /*yield*/, axios_1.default.post(url, payload)]; case 2: response = _b.sent(); return [2 /*return*/, { content: [ { type: 'text', text: JSON.stringify({ success: true, message: 'Mensagem webhook enviada', status: response.status, }, null, 2), }, ], }]; case 3: error_3 = _b.sent(); throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, "Erro ao enviar webhook: ".concat(error_3 instanceof Error ? error_3.message : String(error_3))); case 4: return [2 /*return*/]; } }); }); }; SlackMCPServer.prototype.getChannelAnalytics = function (args) { return __awaiter(this, void 0, void 0, function () { var _a, channel, _b, days, oldest, result, messages, userStats, hourStats, totalReactions; return __generator(this, function (_c) { switch (_c.label) { case 0: _a = SlackAnalyticsSchema.parse(args), channel = _a.channel, _b = _a.days, days = _b === void 0 ? 7 : _b; oldest = Math.floor((Date.now() - days * 24 * 60 * 60 * 1000) / 1000); return [4 /*yield*/, this.slackClient.conversations.history({ channel: channel, oldest: oldest.toString(), limit: 1000, })]; case 1: result = _c.sent(); messages = result.messages || []; userStats = {}; hourStats = {}; totalReactions = 0; messages.forEach(function (message) { if (message.user) { userStats[message.user] = (userStats[message.user] || 0) + 1; } if (message.ts) { var hour = new Date(parseFloat(message.ts) * 1000).getHours(); hourStats[hour] = (hourStats[hour] || 0) + 1; } if (message.reactions) { totalReactions += message.reactions.reduce(function (sum, reaction) { return sum + (reaction.count || 0); }, 0); } }); return [2 /*return*/, { content: [ { type: 'text', text: JSON.stringify({ success: true, analytics: { period_days: days, total_messages: messages.length, total_reactions: totalReactions, most_active_users: Object.entries(userStats) .sort(function (_a, _b) { var a = _a[1]; var b = _b[1]; return b - a; }) .slice(0, 5), activity_by_hour: hourStats, engagement_rate: messages.length > 0 ? totalReactions / messages.length : 0, }, }, null, 2), }, ], }]; } }); }); }; SlackMCPServer.prototype.mentionUsers = function (args) { return __awaiter(this, void 0, void 0, function () { var _a, channel, text, users, mentions, messageText, result; return __generator(this, function (_b) { switch (_b.label) { case 0: _a = SlackMentionSchema.parse(args), channel = _a.channel, text = _a.text, users = _a.users; mentions = users.map(function (user) { return "<@".concat(user, ">"); }).join(' '); messageText = "".concat(mentions, " ").concat(text); return [4 /*yield*/, this.slackClient.chat.postMessage({ channel: channel, text: messageText, })]; case 1: result = _b.sent(); return [2 /*return*/, { content: [ { type: 'text', text: JSON.stringify({ success: true, message: 'Mensagem com menções enviada', mentioned_users: users.length, message_ts: result.ts, }, null, 2), }, ], }]; } }); }); }; SlackMCPServer.prototype.archiveOldChannels = function (args) { return __awaiter(this, void 0, void 0, function () { var _a, days_inactive, _b, dry_run, cutoffDate, channelsResult, inactiveChannels, _i, _c, channel, history_1, lastMessage; var _d; return __generator(this, function (_e) { switch (_e.label) { case 0: _a = args.days_inactive, days_inactive = _a === void 0 ? 30 : _a, _b = args.dry_run, dry_run = _b === void 0 ? true : _b; cutoffDate = Math.floor((Date.now() - days_inactive * 24 * 60 * 60 * 1000) / 1000); return [4 /*yield*/, this.slackClient.conversations.list({ types: 'public_channel,private_channel', limit: 100, })]; case 1: channelsResult = _e.sent(); inactiveChannels = []; _i = 0, _c = channelsResult.channels || []; _e.label = 2; case 2: if (!(_i < _c.length)) return [3 /*break*/, 6]; channel = _c[_i]; if (!channel.id) return [3 /*break*/, 5]; return [4 /*yield*/, this.slackClient.conversations.history({ channel: channel.id, limit: 1, })]; case 3: history_1 = _e.sent(); lastMessage = (_d = history_1.messages) === null || _d === void 0 ? void 0 : _d[0]; if (!(!(lastMessage === null || lastMessage === void 0 ? void 0 : lastMessage.ts) || parseFloat(lastMessage.ts) < cutoffDate)) return [3 /*break*/, 5]; inactiveChannels.push({ id: channel.id, name: channel.name, last_activity: (lastMessage === null || lastMessage === void 0 ? void 0 : lastMessage.ts) ? new Date(parseFloat(lastMessage.ts) * 1000).toISOString() : 'Never', }); if (!(!dry_run && channel.id)) return [3 /*break*/, 5]; return [4 /*yield*/, this.slackClient.conversations.archive({ channel: channel.id, })]; case 4: _e.sent(); _e.label = 5; case 5: _i++; return [3 /*break*/, 2]; case 6: return [2 /*return*/, { content: [ { type: 'text', text: JSON.stringify({ success: true, dry_run: dry_run, inactive_channels: inactiveChannels.length, channels: inactiveChannels, message: dry_run ? 'Simulação concluída' : "".concat(inactiveChannels.length, " canais arquivados"), }, null, 2), }, ], }]; } }); }); }; SlackMCPServer.prototype.bulkInviteUsers = function (args) { return __awaiter(this, void 0, void 0, function () { var channel, users, results, _i, users_1, user, error_4, successful; return __generator(this, function (_a) { switch (_a.label) { case 0: channel = args.channel, users = args.users; results = []; _i = 0, users_1 = users; _a.label = 1; case 1: if (!(_i < users_1.length)) return [3 /*break*/, 6]; user = users_1[_i]; _a.label = 2; case 2: _a.trys.push([2, 4, , 5]); return [4 /*yield*/, this.slackClient.conversations.invite({ channel: channel, users: user, })]; case 3: _a.sent(); results.push({ user: user, success: true }); return [3 /*break*/, 5]; case 4: error_4 = _a.sent(); results.push({ user: user, success: false, error: error_4 instanceof Error ? error_4.message : String(error_4) }); return [3 /*break*/, 5]; case 5: _i++; return [3 /*break*/, 1]; case 6: successful = results.filter(function (r) { return r.success; }).length; return [2 /*return*/, { content: [ { type: 'text', text: JSON.stringify({ success: true, invited_users: successful, total_users: users.length, results: results, }, null, 2), }, ], }]; } }); }); }; SlackMCPServer.prototype.getPrivateChannelsWithUser = function (args) { return __awaiter(this, void 0, void 0, function () { var user_id, _a, include_history, result, privateChannels, _i, _b, channel, members, channelInfo, history_2, lastMessage, historyError_1, error_5; var _c, _d, _e, _f; return __generator(this, function (_g) { switch (_g.label) { case 0: user_id = args.user_id, _a = args.include_history, include_history = _a === void 0 ? false : _a; return [4 /*yield*/, this.slackClient.conversations.list({ types: 'private_channel,mpim', limit: 200, })]; case 1: result = _g.sent(); privateChannels = []; _i = 0, _b = result.channels || []; _g.label = 2; case 2: if (!(_i < _b.length)) return [3 /*break*/, 12]; channel = _b[_i]; if (!channel.id) return [3 /*break*/, 11]; _g.label = 3; case 3: _g.trys.push([3, 10, , 11]); if (!user_id) return [3 /*break*/, 5]; return [4 /*yield*/, this.slackClient.conversations.members({ channel: channel.id, })]; case 4: members = _g.sent(); if (!((_c = members.members) === null || _c === void 0 ? void 0 : _c.includes(user_id))) { return [3 /*break*/, 11]; // Usuário não está neste canal } _g.label = 5; case 5: channelInfo = { id: channel.id, name: channel.name, is_group: channel.is_group, is_mpim: channel.is_mpim, num_members: channel.num_members, topic: (_d = channel.topic) === null || _d === void 0 ? void 0 : _d.value, purpose: (_e = channel.purpose) === null || _e === void 0 ? void 0 : _e.value, }; if (!include_history) return [3 /*break*/, 9]; _g.label = 6; case 6: _g.trys.push([6, 8, , 9]); return [4 /*yield*/, this.slackClient.conversations.history({ channel: channel.id, limit: 1, })]; case 7: history_2 = _g.sent(); lastMessage = (_f = history_2.messages) === null || _f === void 0 ? void 0 : _f[0]; if (lastMessage) { channelInfo.last_message = { text: lastMessage.text, user: lastMessage.user, ts: lastMessage.ts, date: new Date(parseFloat(lastMessage.ts || '0') * 1000).toISOString(), }; } return [3 /*break*/, 9]; case 8: historyError_1 = _g.sent(); channelInfo.last_message = null; return [3 /*break*/, 9]; case 9: privateChannels.push(channelInfo); return [3 /*break*/, 11]; case 10: error_5 = _g.sent(); console.error("Erro ao processar canal ".concat(channel.id, ":"), error_5); return [3 /*break*/, 11]; case 11: _i++; return [3 /*break*/, 2]; case 12: return [2 /*return*/, { content: [ { type: 'text', text: JSON.stringify({ success: true, user_id: user_id || 'bot_user', private_channels_count: privateChannels.length, private_channels: privateChannels, }, null, 2), }, ], }]; } }); }); }; SlackMCPServer.prototype.searchInPrivateGroups = function (args) { return __awaiter(this, void 0, void 0, function () { var query, group_ids, _a, count, results, _loop_1, this_1, _i, group_ids_1, groupId, searchQuery, result, matches, error_6; var _b, _c, _d, _e, _f; return __generator(this, function (_g) { switch (_g.label) { case 0: query = args.query, group_ids = args.group_ids, _a = args.count, count = _a === void 0 ? 20 : _a; _g.label = 1; case 1: _g.trys.push([1, 8, , 9]); if (!(group_ids && group_ids.length > 0)) return [3 /*break*/, 6]; results = []; _loop_1 = function (groupId) { var searchQuery_1, result_1, matches_1, error_7; return __generator(this, function (_h) { switch (_h.label) { case 0: _h.trys.push([0, 2, , 3]); searchQuery_1 = "in:".concat(groupId, " ").concat(query); return [4 /*yield*/, this_1.slackClient.search.messages({ query: searchQuery_1, count: Math.floor(count / group_ids.length), })]; case 1: result_1 = _h.sent(); matches_1 = ((_c = (_b = result_1.messages) === null || _b === void 0 ? void 0 : _b.matches) === null || _c === void 0 ? void 0 : _c.map(function (match) { var _a; return ({ text: match.text, user: match.username, channel: (_a = match.channel) === null || _a === void 0 ? void 0 : _a.name, channel_id: groupId, ts: match.ts, permalink: match.permalink, }); })) || []; results.push.apply(results, matches_1); return [3 /*break*/, 3]; case 2: error_7 = _h.sent(); console.error("Erro ao buscar no grupo ".concat(groupId, ":"), error_7); return [3 /*break*/, 3]; case 3: return [2 /*return*/]; } }); }; this_1 = this; _i = 0, group_ids_1 = group_ids; _g.label = 2; case 2: if (!(_i < group_ids_1.length)) return [3 /*break*/, 5]; groupId = group_ids_1[_i]; return [5 /*yield**/, _loop_1(groupId)]; case 3: _g.sent(); _g.label = 4; case 4: _i++; return [3 /*break*/, 2]; case 5: return [2 /*return*/, { content: [ { type: 'text', text: JSON.stringify({ success: true, query: query, searched_groups: group_ids, matches_count: results.length, matches: results.slice(0, count), }, null, 2), }, ], }]; case 6: searchQuery = "".concat(query, " in:private"); return [4 /*yield*/, this.slackClient.search.messages({ query: searchQuery, count: count, })]; case 7: result = _g.sent(); matches = ((_e = (_d = result.messages) === null || _d === void 0 ? void 0 : _d.matches) === null || _e === void 0 ? void 0 : _e.map(function (match) { var _a; return ({ text: match.text, user: match.username, channel: (_a = match.channel) === null || _a === void 0 ? void 0 : _a.name, ts: match.ts, permalink: match.permalink, is_private: true, }); })) || []; return [2 /*return*/, { content: [ { type: 'text', text: JSON.stringify({ success: true, query: query, search_scope: 'private_channels', total_matches: (_f = result.messages) === null || _f === void 0 ? void 0 : _f.total, matches_count: matches.length, matches: matches, }, null, 2), }, ], }]; case 8: error_6 = _g.sent(); throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, "Erro ao buscar em grupos privados: ".concat(error_6 instanceof Error ? error_6.message : String(error_6))); case 9: return [2 /*return*/]; } }); }); }; SlackMCPServer.prototype.run = function () { return __awaiter(this, void 0, void 0, function () { var transport; return __generator(this, function (_a) { switch (_a.label) { case 0: transport = new stdio_js_1.StdioServerTransport(); return [4 /*yield*/, this.server.connect(transport)]; case 1: _a.sent(); console.error('Slack MCP Server iniciado'); return [2 /*return*/]; } }); }); }; return SlackMCPServer; }()); var server = new SlackMCPServer(); server.run().catch(console.error);

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/will2023a/MCP-SLACK'

If you have feedback or need assistance with the MCP directory API, please join our Discord server