mcp-lab-agent 2.1.4 → 2.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,73 @@
1
+ # Troubleshooting — QA Lab Slack Bot
2
+
3
+ ## Bot não responde quando envio mensagem
4
+
5
+ ### 1. Bot foi convidado para o canal?
6
+
7
+ **Muito comum.** O bot precisa estar no canal para receber menções.
8
+
9
+ - No canal: digite `/invite @NomeDoSeuBot`
10
+ - Ou mencione em DM: abra DM com o bot e teste `@Bot analise o projeto`
11
+
12
+ ### 2. Configuração no Slack (api.slack.com)
13
+
14
+ Seguindo a [documentação oficial](https://docs.slack.dev/app-management/quickstart-app-settings):
15
+
16
+ #### Se usa **Socket Mode** (PC corporativo, sem ngrok):
17
+
18
+ 1. **Socket Mode** → Enable Socket Mode → ON
19
+ 2. **Basic Information** → App-Level Tokens → Generate → scope: `connections:write` → copie (`xapp-...`)
20
+ 3. **Event Subscriptions** → Enable Events → Subscribe to bot events → `app_mention`
21
+ 4. **OAuth & Permissions** → Bot Token Scopes: `app_mentions:read`, `chat:write`, `channels:read`, `channels:history`
22
+ 5. **Install App** → **Reinstall to Workspace** (obrigatório após alterar scopes ou eventos)
23
+
24
+ #### Se usa **HTTP** (ngrok):
25
+
26
+ 1. **Event Subscriptions** → Enable Events → Request URL: `https://SEU_NGROK.ngrok.io/slack/events`
27
+ 2. Subscribe to bot events: `app_mention`
28
+ 3. **Basic Information** → App Credentials → Signing Secret (Show)
29
+
30
+ ### 3. Config correta no mcp.json
31
+
32
+ O `~/.cursor/mcp.json` precisa ter a seção `qa-lab-agent.slack`:
33
+
34
+ ```json
35
+ {
36
+ "qa-lab-agent": {
37
+ "slack": {
38
+ "botToken": "xoxb-...",
39
+ "appToken": "xapp-...",
40
+ "useLocal": true
41
+ }
42
+ }
43
+ }
44
+ ```
45
+
46
+ - `botToken` — OAuth & Permissions → OAuth Tokens → Bot User OAuth Token (começa com `xoxb-`)
47
+ - `appToken` — Basic Information → App-Level Tokens (scope `connections:write`, começa com `xapp-`) — só para Socket Mode
48
+ - `useLocal: true` — analisa o projeto local (pasta atual) em vez de clonar um repo
49
+
50
+ ### 4. Rodar o diagnóstico
51
+
52
+ ```bash
53
+ cd slack-bot
54
+ npm run check
55
+ ```
56
+
57
+ Corrija qualquer item marcado com ❌.
58
+
59
+ ### 5. Conferir se o bot está rodando
60
+
61
+ Ao subir com `npm start`, deve aparecer:
62
+
63
+ - Socket Mode: `QA Lab Slack Bot rodando em Socket Mode (sem URL pública necessária)`
64
+ - HTTP: `QA Lab Slack Bot rodando em http://localhost:3000`
65
+
66
+ Se der erro ao iniciar, leia a mensagem — geralmente indica token inválido ou faltando.
67
+
68
+ ### 6. Firewall / proxy corporativo
69
+
70
+ Em redes corporativas, às vezes o WebSocket (Socket Mode) é bloqueado. Tente:
71
+
72
+ - Socket Mode primeiro (não precisa de URL pública)
73
+ - Se não funcionar, use ngrok em outra máquina e configure HTTP
@@ -3,54 +3,74 @@
3
3
  * Verifica se a config do Slack está correta.
4
4
  * Rode: node check-config.js
5
5
  */
6
+ import { config } from "dotenv";
6
7
  import { readFileSync, existsSync } from "node:fs";
7
8
  import path from "node:path";
8
9
  import { fileURLToPath } from "node:url";
9
10
 
10
11
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
11
- const home = process.env.HOME || process.env.USERPROFILE;
12
- const mcpPath = home ? path.join(home, ".cursor", "mcp.json") : null;
13
-
14
- console.log("\n🔧 QA Lab Slack Bot - Diagnóstico\n");
15
- console.log("1. mcp.json:");
16
- if (!mcpPath || !existsSync(mcpPath)) {
17
- console.log(" ❌ Não encontrado em", mcpPath || "(HOME não definido)");
18
- process.exit(1);
12
+ // check-config.js fica em slack-bot/, então __dirname já é a pasta slack-bot
13
+ const SLACK_BOT_DIR = __dirname;
14
+ // Carrega .env na mesma ordem do config.js (QA_LAB_ENV override, cwd, slack-bot)
15
+ const envPaths = [
16
+ process.env.QA_LAB_ENV,
17
+ path.join(process.cwd(), ".env"),
18
+ path.join(SLACK_BOT_DIR, ".env"),
19
+ ].filter(Boolean);
20
+ for (const p of envPaths) {
21
+ if (p && existsSync(p)) {
22
+ config({ path: p });
23
+ break;
24
+ }
25
+ }
26
+ function getMcpJsonPath() {
27
+ const home = process.env.HOME || process.env.USERPROFILE;
28
+ return home ? path.join(home, ".cursor", "mcp.json") : null;
19
29
  }
20
- console.log(" ✅ Encontrado:", mcpPath);
30
+ const mcpPath = process.env.QA_LAB_MCP_CONFIG || getMcpJsonPath();
21
31
 
22
- let mcp;
23
- try {
24
- mcp = JSON.parse(readFileSync(mcpPath, "utf8"));
25
- } catch (e) {
26
- console.log(" Erro ao ler JSON:", e.message);
27
- process.exit(1);
32
+ console.log("\n🔧 QA Lab Slack Bot - Diagnóstico\n");
33
+ console.log("1. Origens de config (mcp.json ou .env):");
34
+ let mcp = null;
35
+ if (mcpPath && existsSync(mcpPath)) {
36
+ console.log(" mcp.json:", mcpPath);
37
+ try {
38
+ mcp = JSON.parse(readFileSync(mcpPath, "utf8"));
39
+ } catch (e) {
40
+ console.log(" ❌ Erro ao ler mcp.json:", e.message);
41
+ }
42
+ } else {
43
+ console.log(" ⊘ mcp.json não encontrado (use .env na pasta do slack-bot ou cwd)");
28
44
  }
29
45
 
30
- const slack = mcp?.["qa-lab-agent"]?.slack;
31
- if (!slack) {
32
- console.log(" ❌ Seção 'qa-lab-agent.slack' não encontrada");
33
- console.log(" Estrutura esperada:");
34
- console.log(' { "qa-lab-agent": { "slack": { "botToken": "xoxb-...", "signingSecret": "..." } } }');
35
- process.exit(1);
46
+ const slack = mcp?.["qa-lab-agent"]?.slack || {};
47
+ const hasMcpSlack = !!mcp?.["qa-lab-agent"]?.slack;
48
+ if (!hasMcpSlack) {
49
+ console.log(" ⚠️ Seção 'qa-lab-agent.slack' não encontrada no mcp.json");
50
+ console.log(" Usando .env como fallback (SLACK_BOT_TOKEN, SLACK_APP_TOKEN ou SLACK_SIGNING_SECRET)");
36
51
  }
37
- console.log(" ✅ Config slack encontrada");
52
+ if (hasMcpSlack) console.log(" ✅ Config slack encontrada no mcp.json");
38
53
 
39
54
  console.log("\n2. botToken:");
40
- const token = slack.botToken || slack.SLACK_BOT_TOKEN;
55
+ const token = slack.botToken || slack.SLACK_BOT_TOKEN || process.env.SLACK_BOT_TOKEN;
41
56
  if (!token) {
42
57
  console.log(" ❌ Ausente. Adicione 'botToken' ou 'SLACK_BOT_TOKEN'");
43
58
  } else if (!token.startsWith("xoxb-")) {
44
59
  console.log(" ⚠️ Deve começar com 'xoxb-'. Você usou o Client Secret?");
45
- console.log(" Use: OAuth & Permissions → Bot User OAuth Token (depois de Install to Workspace)");
60
+ console.log(" Onde: OAuth & Permissions → OAuth Tokens → Bot User OAuth Token");
46
61
  } else {
47
62
  console.log(" ✅ OK (xoxb-...)");
48
63
  }
49
64
 
50
- console.log("\n3. signingSecret:");
51
- const secret = slack.signingSecret || slack.SLACK_SIGNING_SECRET;
52
- if (!secret) {
53
- console.log(" ❌ Ausente. Adicione 'signingSecret'");
65
+ const appToken = slack.appToken || slack.slack_app_token || slack.SLACK_APP_TOKEN || process.env.SLACK_APP_TOKEN;
66
+ const useSocketMode = !!(appToken && appToken.startsWith("xapp-"));
67
+
68
+ console.log("\n3. signingSecret (só para modo HTTP):");
69
+ const secret = slack.signingSecret || slack.SLACK_SIGNING_SECRET || process.env.SLACK_SIGNING_SECRET;
70
+ if (useSocketMode) {
71
+ console.log(" ⊘ Não necessário (você está usando Socket Mode)");
72
+ } else if (!secret) {
73
+ console.log(" ❌ Ausente. Adicione 'signingSecret' ou use appToken para Socket Mode");
54
74
  console.log(" Onde: Basic Information → App Credentials → Signing Secret (Show)");
55
75
  } else if (secret === "..." || secret.length < 20) {
56
76
  console.log(" ⚠️ Parece placeholder ou inválido. Use o valor real do Signing Secret.");
@@ -58,17 +78,40 @@ if (!secret) {
58
78
  console.log(" ✅ OK");
59
79
  }
60
80
 
61
- console.log("\n4. Event Subscriptions (api.slack.com):");
62
- console.log(" • Request URL deve ser: https://SEU_DOMINIO/slack/events");
63
- console.log(" • Se local: use ngrok → ngrok http 3000");
64
- console.log(" Bot event: app_mention");
81
+ console.log("\n4. appToken (slack_app_token) para Socket Mode:");
82
+ if (appToken) {
83
+ if (appToken.startsWith("xapp-")) {
84
+ console.log(" OK (xapp-...) — bot funcionará sem URL pública");
85
+ } else {
86
+ console.log(" ⚠️ Deve começar com 'xapp-'. Onde: Basic Information → App-Level Tokens (scope: connections:write)");
87
+ }
88
+ } else {
89
+ console.log(" ⊘ Não configurado. Onde: Basic Information → App-Level Tokens (scope: connections:write)");
90
+ }
91
+
92
+ console.log("\n5. Resumo do modo:");
93
+ if (useSocketMode) {
94
+ console.log(" ✅ Socket Mode (appToken xapp-) — não precisa de URL pública");
95
+ console.log(" • Em api.slack.com: Socket Mode → Enable");
96
+ console.log(" • Basic Information → App-Level Tokens → Generate com scope connections:write");
97
+ } else {
98
+ console.log(" HTTP (Event Subscriptions):");
99
+ console.log(" • Request URL: https://SEU_DOMINIO/slack/events");
100
+ console.log(" • Se local: ngrok http 3000");
101
+ console.log(" • Bot event: app_mention");
102
+ console.log(" 💡 Para PC corporativo: use appToken (Socket Mode) e não precisa de ngrok!");
103
+ }
65
104
 
66
- console.log("\n5. Bot no canal:");
67
- console.log(" • Mencione o bot no canal ou use /invite @NomeDoBot");
105
+ console.log("\n6. Bot no canal:");
106
+ console.log(" • /invite @NomeDoBot (obrigatório!) ou mencione em DM");
68
107
 
69
- if (token && secret && token.startsWith("xoxb-")) {
70
- console.log("\n✅ Config parece OK. Rode: npm start");
108
+ if (token && token.startsWith("xoxb-") && (useSocketMode || secret)) {
109
+ console.log("\n✅ Config OK. Rode: npm start");
110
+ console.log(" Modo:", useSocketMode ? "Socket (qualquer ambiente)" : "HTTP (ngrok)");
71
111
  } else {
72
- console.log("\n❌ Corrija os itens acima e tente novamente.");
112
+ console.log("\n❌ Corrija os itens acima.");
113
+ if (!token) console.log(" Dica: botToken ou SLACK_BOT_TOKEN obrigatório.");
114
+ if (!useSocketMode && !secret) console.log(" Dica: use appToken (Socket) OU signingSecret (HTTP).");
115
+ console.log(" Onde obter: slack-bot/CREDENTIALS.md | https://docs.slack.dev/app-management/quickstart-app-settings");
73
116
  }
74
117
  console.log("");
@@ -14,21 +14,27 @@ console.log("\n🔧 QA Lab Slack Bot - Setup\n");
14
14
 
15
15
  if (!existsSync(envPath)) {
16
16
  copyFileSync(path.join(__dirname, ".env.example"), envPath);
17
- console.log("✅ .env criado. Edite e preencha:");
18
- console.log(" - SLACK_BOT_TOKEN (api.slack.com → sua app → OAuth)");
19
- console.log(" - SLACK_SIGNING_SECRET (api.slack.com → Basic Information)\n");
17
+ console.log("✅ .env criado. Edite e preencha:\n");
18
+ console.log(" OBRIGATÓRIO: SLACK_BOT_TOKEN (xoxb-...)\n");
19
+ console.log(" Escolha UM modo:\n");
20
+ console.log(" • Socket Mode (qualquer ambiente): SLACK_APP_TOKEN (xapp-...)");
21
+ console.log(" • HTTP (ngrok): SLACK_SIGNING_SECRET\n");
20
22
  process.exit(0);
21
23
  }
22
24
 
23
25
  const env = readFileSync(envPath, "utf8");
24
26
  const hasToken = /SLACK_BOT_TOKEN=xoxb-/.test(env);
27
+ const hasAppToken = /SLACK_APP_TOKEN=xapp-/.test(env);
25
28
  const hasSecret = /SLACK_SIGNING_SECRET=.{20,}/.test(env);
26
29
 
27
- if (!hasToken || !hasSecret) {
28
- console.log("⚠️ .env existe mas faltam valores:");
29
- if (!hasToken) console.log(" - SLACK_BOT_TOKEN");
30
- if (!hasSecret) console.log(" - SLACK_SIGNING_SECRET");
31
- console.log("\nEdite slack-bot/.env e preencha os valores.\n");
30
+ if (!hasToken) {
31
+ console.log("⚠️ .env existe mas falta SLACK_BOT_TOKEN (xoxb-...)\n");
32
+ process.exit(1);
33
+ }
34
+ if (!hasAppToken && !hasSecret) {
35
+ console.log("⚠️ Configure um modo de conexão:");
36
+ console.log(" • Socket Mode (recomendado): SLACK_APP_TOKEN=xapp-...");
37
+ console.log(" • HTTP: SLACK_SIGNING_SECRET=...\n");
32
38
  process.exit(1);
33
39
  }
34
40
 
@@ -7,7 +7,19 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
7
  const ROOT = path.resolve(__dirname, "../..");
8
8
  const SLACK_BOT_DIR = path.dirname(__dirname);
9
9
 
10
- config({ path: path.join(SLACK_BOT_DIR, ".env") });
10
+ // Carrega .env de múltiplos locais (funciona em pessoal, corporativo, npx, etc.)
11
+ // Ordem: cwd primeiro (override), depois package dir, depois QA_LAB_ENV
12
+ const envPaths = [
13
+ process.env.QA_LAB_ENV,
14
+ path.join(process.cwd(), ".env"),
15
+ path.join(SLACK_BOT_DIR, ".env"),
16
+ ].filter(Boolean);
17
+ for (const p of envPaths) {
18
+ if (p && existsSync(p)) {
19
+ config({ path: p });
20
+ break;
21
+ }
22
+ }
11
23
 
12
24
  function getMcpJsonPath() {
13
25
  const home = process.env.HOME || process.env.USERPROFILE;
@@ -33,6 +45,7 @@ function getSlackConfigFromMcp() {
33
45
  return {
34
46
  id: slack.id || slack.channelId,
35
47
  botToken: slack.botToken || slack.SLACK_BOT_TOKEN,
48
+ appToken: slack.appToken || slack.slack_app_token || slack.SLACK_APP_TOKEN,
36
49
  signingSecret: slack.signingSecret || slack.SLACK_SIGNING_SECRET,
37
50
  repo: slack.repo || slack.REPO_URL,
38
51
  branch: slack.branch || slack.REPO_BRANCH || "main",
@@ -54,13 +67,10 @@ function loadSlackConfig() {
54
67
 
55
68
  function getSlackTokens() {
56
69
  const fromMcp = getSlackConfigFromMcp();
57
- if (fromMcp?.botToken && fromMcp?.signingSecret) {
58
- return { token: fromMcp.botToken, signingSecret: fromMcp.signingSecret };
59
- }
60
- return {
61
- token: process.env.SLACK_BOT_TOKEN,
62
- signingSecret: process.env.SLACK_SIGNING_SECRET,
63
- };
70
+ const token = fromMcp?.botToken || process.env.SLACK_BOT_TOKEN;
71
+ const signingSecret = fromMcp?.signingSecret || process.env.SLACK_SIGNING_SECRET;
72
+ const appToken = fromMcp?.appToken || process.env.SLACK_APP_TOKEN;
73
+ return { token, signingSecret, appToken };
64
74
  }
65
75
 
66
76
  function getRepoForChannel() {
@@ -4,28 +4,62 @@
4
4
  * Recebe @mentions no Slack, executa mcp-lab-agent e posta relatório.
5
5
  *
6
6
  * Config: ~/.cursor/mcp.json (qa-lab-agent.slack) ou .env
7
+ *
8
+ * Dois modos:
9
+ * 1) Socket Mode (recomendado para PC corporativo): botToken + appToken (slack_app_token)
10
+ * Não precisa de URL pública. Funciona atrás de firewall.
11
+ * 2) HTTP (Event Subscriptions): botToken + signingSecret
12
+ * Precisa de URL pública (ngrok) em api.slack.com.
7
13
  */
8
14
  import { App } from "@slack/bolt";
9
15
  import { getSlackTokens } from "./config.js";
10
16
  import { registerAppMention } from "./handlers/app-mention.js";
11
17
 
12
- const { token, signingSecret } = getSlackTokens();
18
+ const { token, signingSecret, appToken } = getSlackTokens();
19
+
20
+ const useSocketMode = !!(appToken && appToken.startsWith("xapp-"));
21
+
22
+ if (!token || !token.startsWith("xoxb-")) {
23
+ console.error(
24
+ "Configure botToken (xoxb-...) no ~/.cursor/mcp.json ou .env (SLACK_BOT_TOKEN).\n" +
25
+ "Onde: api.slack.com → sua app → OAuth & Permissions → Bot User OAuth Token"
26
+ );
27
+ process.exit(1);
28
+ }
13
29
 
14
- if (!token || !signingSecret) {
15
- console.error("Configure no ~/.cursor/mcp.json:\n \"qa-lab-agent\": { \"slack\": { \"botToken\": \"xoxb-...\", \"signingSecret\": \"...\" } }\n\nOu use .env (SLACK_BOT_TOKEN, SLACK_SIGNING_SECRET)");
30
+ if (useSocketMode) {
31
+ // Socket Mode: funciona sem URL pública (ideal para PC corporativo)
32
+ if (!appToken.startsWith("xapp-")) {
33
+ console.error("appToken (slack_app_token) deve começar com 'xapp-' para Socket Mode.");
34
+ process.exit(1);
35
+ }
36
+ } else if (!signingSecret) {
37
+ console.error(
38
+ "Modo HTTP: configure signingSecret no ~/.cursor/mcp.json ou .env (SLACK_SIGNING_SECRET).\n" +
39
+ "Modo Socket: adicione appToken (xapp-...) para funcionar sem URL pública."
40
+ );
16
41
  process.exit(1);
17
42
  }
18
43
 
19
- const app = new App({
44
+ const appOptions = {
20
45
  token,
21
- signingSecret,
22
- });
46
+ ...(useSocketMode
47
+ ? { socketMode: true, appToken }
48
+ : { signingSecret }),
49
+ };
23
50
 
51
+ const app = new App(appOptions);
24
52
  registerAppMention(app);
25
53
 
26
- const port = process.env.PORT || 3000;
27
- const server = await app.start(port);
28
- console.log(`QA Lab Slack Bot rodando em http://localhost:${port}`);
29
- console.log("Configure Event Subscriptions em api.slack.com com:");
30
- console.log(` Request URL: https://SEU_DOMINIO/slack/events`);
31
- console.log(" Bot event: app_mention");
54
+ if (useSocketMode) {
55
+ await app.start();
56
+ console.log("QA Lab Slack Bot rodando em Socket Mode (sem URL pública necessária)");
57
+ console.log("Mencione o bot em um canal para usar.");
58
+ } else {
59
+ const port = process.env.PORT || 3000;
60
+ await app.start(port);
61
+ console.log(`QA Lab Slack Bot rodando em http://localhost:${port}`);
62
+ console.log("Configure Event Subscriptions em api.slack.com:");
63
+ console.log(` Request URL: https://SEU_DOMINIO/slack/events`);
64
+ console.log(" Bot event: app_mention");
65
+ }