#!/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);