mintboard-mcp 0.1.0
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.
- package/README.md +56 -0
- package/dist/index.js +140 -0
- package/package.json +33 -0
package/README.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# mintboard-mcp
|
|
2
|
+
|
|
3
|
+
Servidor MCP (Model Context Protocol) do MintBoard. Conecta o CRM a
|
|
4
|
+
assistentes de IA — Claude Code, Claude Desktop, Gemini CLI e qualquer
|
|
5
|
+
outro cliente MCP — usando a API key da organização.
|
|
6
|
+
|
|
7
|
+
## Ferramentas
|
|
8
|
+
|
|
9
|
+
| Tool | Descrição |
|
|
10
|
+
|------|-----------|
|
|
11
|
+
| `list_projects` / `create_project` / `update_project` | Projetos |
|
|
12
|
+
| `list_tasks` / `create_task` / `update_task` | Tarefas |
|
|
13
|
+
| `list_pipeline` / `create_lead` / `move_lead` | Funil de vendas |
|
|
14
|
+
| `finance_overview` | Visão financeira |
|
|
15
|
+
| `project_summary` | Resumo executivo por IA |
|
|
16
|
+
|
|
17
|
+
## Configuração
|
|
18
|
+
|
|
19
|
+
Variáveis de ambiente:
|
|
20
|
+
|
|
21
|
+
- `MINTBOARD_API_KEY` — gere em **Configurações → Integrações** (admin)
|
|
22
|
+
- `MINTBOARD_API_URL` — opcional; default produção
|
|
23
|
+
|
|
24
|
+
### Claude Code
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
claude mcp add mintboard \
|
|
28
|
+
--env MINTBOARD_API_KEY=org_... \
|
|
29
|
+
-- npx -y mintboard-mcp
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Claude Desktop / Gemini CLI (JSON)
|
|
33
|
+
|
|
34
|
+
```json
|
|
35
|
+
{
|
|
36
|
+
"mcpServers": {
|
|
37
|
+
"mintboard": {
|
|
38
|
+
"command": "npx",
|
|
39
|
+
"args": ["-y", "mintboard-mcp"],
|
|
40
|
+
"env": { "MINTBOARD_API_KEY": "org_..." }
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Sem publicar no npm (rodando do repositório)
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
cd packages/mcp-server && npm run build
|
|
50
|
+
# no cliente MCP use:
|
|
51
|
+
# command: node
|
|
52
|
+
# args: ["<repo>/packages/mcp-server/dist/index.js"]
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
> Para o `npx -y mintboard-mcp` funcionar, publique o pacote:
|
|
56
|
+
> `cd packages/mcp-server && npm publish --access public`
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Servidor MCP do MintBoard (stdio).
|
|
4
|
+
*
|
|
5
|
+
* Expõe o CRM como ferramentas para qualquer cliente MCP (Claude Code,
|
|
6
|
+
* Claude Desktop, Gemini CLI, etc). Autentica na API REST com a API key
|
|
7
|
+
* da organização:
|
|
8
|
+
*
|
|
9
|
+
* MINTBOARD_API_KEY — chave da org (Configurações → Integrações)
|
|
10
|
+
* MINTBOARD_API_URL — base da API (default: produção)
|
|
11
|
+
*/
|
|
12
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
13
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
14
|
+
import { z } from "zod";
|
|
15
|
+
const API_URL = process.env.MINTBOARD_API_URL ?? "https://mintboard-production.up.railway.app/api/v1";
|
|
16
|
+
const API_KEY = process.env.MINTBOARD_API_KEY;
|
|
17
|
+
if (!API_KEY) {
|
|
18
|
+
console.error("MINTBOARD_API_KEY ausente. Gere a chave em Configurações → Integrações no MintBoard.");
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
async function call(method, path, body) {
|
|
22
|
+
const res = await fetch(`${API_URL}${path}`, {
|
|
23
|
+
method,
|
|
24
|
+
headers: {
|
|
25
|
+
Authorization: `Bearer ${API_KEY}`,
|
|
26
|
+
"Content-Type": "application/json",
|
|
27
|
+
},
|
|
28
|
+
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
29
|
+
});
|
|
30
|
+
const text = await res.text();
|
|
31
|
+
if (!res.ok) {
|
|
32
|
+
throw new Error(`MintBoard API ${res.status}: ${text.slice(0, 400)}`);
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
return JSON.parse(text);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return text;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function asResult(data) {
|
|
42
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
43
|
+
}
|
|
44
|
+
const server = new McpServer({ name: "mintboard", version: "0.1.0" });
|
|
45
|
+
/* ── Projetos ── */
|
|
46
|
+
server.tool("list_projects", "Lista os projetos da agência (nome, status, prioridade, progresso, prazo)", { status: z.enum(["PLANNING", "ACTIVE", "PAUSED", "COMPLETED", "CANCELLED"]).optional() }, async ({ status }) => asResult(await call("GET", `/projects${status ? `?status=${status}` : ""}`)));
|
|
47
|
+
server.tool("create_project", "Cria um projeto novo", {
|
|
48
|
+
name: z.string().min(2),
|
|
49
|
+
description: z.string().optional(),
|
|
50
|
+
priority: z.enum(["P0", "P1", "P2", "P3"]).optional(),
|
|
51
|
+
deadline: z.string().describe("ISO date, ex: 2026-07-15").optional(),
|
|
52
|
+
}, async (args) => asResult(await call("POST", "/projects", args)));
|
|
53
|
+
server.tool("update_project", "Atualiza um projeto (status, prioridade, progresso 0-100, prazo)", {
|
|
54
|
+
project_id: z.string().uuid(),
|
|
55
|
+
status: z.enum(["PLANNING", "ACTIVE", "PAUSED", "COMPLETED", "CANCELLED"]).optional(),
|
|
56
|
+
priority: z.enum(["P0", "P1", "P2", "P3"]).optional(),
|
|
57
|
+
progress: z.number().int().min(0).max(100).optional(),
|
|
58
|
+
deadline: z.string().optional(),
|
|
59
|
+
}, async ({ project_id, ...rest }) => asResult(await call("PATCH", `/projects/${project_id}`, rest)));
|
|
60
|
+
/* ── Tarefas ── */
|
|
61
|
+
server.tool("list_tasks", "Lista tarefas, com filtros opcionais (projeto, status, atrasadas)", {
|
|
62
|
+
project_id: z.string().uuid().optional(),
|
|
63
|
+
status: z.enum(["BACKLOG", "TODO", "IN_PROGRESS", "IN_REVIEW", "DONE"]).optional(),
|
|
64
|
+
overdue: z.boolean().optional(),
|
|
65
|
+
}, async ({ project_id, status, overdue }) => {
|
|
66
|
+
const qs = new URLSearchParams();
|
|
67
|
+
if (project_id)
|
|
68
|
+
qs.set("project_id", project_id);
|
|
69
|
+
if (status)
|
|
70
|
+
qs.set("status", status);
|
|
71
|
+
if (overdue)
|
|
72
|
+
qs.set("overdue", "true");
|
|
73
|
+
return asResult(await call("GET", `/tasks${qs.size ? `?${qs}` : ""}`));
|
|
74
|
+
});
|
|
75
|
+
server.tool("create_task", "Cria uma tarefa em um projeto", {
|
|
76
|
+
title: z.string().min(1),
|
|
77
|
+
project_id: z.string().uuid(),
|
|
78
|
+
description: z.string().optional(),
|
|
79
|
+
priority: z.enum(["P0", "P1", "P2", "P3"]).optional(),
|
|
80
|
+
status: z.enum(["BACKLOG", "TODO", "IN_PROGRESS", "IN_REVIEW", "DONE"]).optional(),
|
|
81
|
+
deadline: z.string().optional(),
|
|
82
|
+
}, async (args) => asResult(await call("POST", "/tasks", args)));
|
|
83
|
+
server.tool("update_task", "Atualiza uma tarefa (status, prioridade, prazo, título)", {
|
|
84
|
+
task_id: z.string().uuid(),
|
|
85
|
+
title: z.string().optional(),
|
|
86
|
+
status: z.enum(["BACKLOG", "TODO", "IN_PROGRESS", "IN_REVIEW", "DONE"]).optional(),
|
|
87
|
+
priority: z.enum(["P0", "P1", "P2", "P3"]).optional(),
|
|
88
|
+
deadline: z.string().optional(),
|
|
89
|
+
}, async ({ task_id, ...rest }) => asResult(await call("PATCH", `/tasks/${task_id}`, rest)));
|
|
90
|
+
/* ── Pipeline (CRM) ── */
|
|
91
|
+
server.tool("list_pipeline", "Lista os estágios do funil e os leads em cada um, com valores", {}, async () => {
|
|
92
|
+
const [stages, leads] = await Promise.all([
|
|
93
|
+
call("GET", "/pipeline/stages"),
|
|
94
|
+
call("GET", "/pipeline/leads"),
|
|
95
|
+
]);
|
|
96
|
+
return asResult({ stages, leads });
|
|
97
|
+
});
|
|
98
|
+
server.tool("create_lead", "Cria um lead no funil de vendas", {
|
|
99
|
+
name: z.string().min(1),
|
|
100
|
+
stage_id: z.string().uuid().describe("ID do estágio (use list_pipeline)"),
|
|
101
|
+
email: z.string().optional(),
|
|
102
|
+
company: z.string().optional(),
|
|
103
|
+
value: z.number().optional().describe("Valor estimado em reais"),
|
|
104
|
+
source: z.enum(["WEBSITE", "REFERRAL", "SOCIAL_MEDIA", "COLD_OUTREACH", "EVENT", "OTHER"]).optional(),
|
|
105
|
+
notes: z.string().optional(),
|
|
106
|
+
}, async (args) => asResult(await call("POST", "/pipeline/leads", args)));
|
|
107
|
+
server.tool("move_lead", "Move um lead para outro estágio do funil", {
|
|
108
|
+
lead_id: z.string().uuid(),
|
|
109
|
+
stage_id: z.string().uuid(),
|
|
110
|
+
}, async ({ lead_id, stage_id }) => asResult(await call("PATCH", `/pipeline/leads/${lead_id}/stage`, { stage_id })));
|
|
111
|
+
/* ── Financeiro ── */
|
|
112
|
+
server.tool("finance_overview", "Visão financeira da agência: a receber, recebido, parcelas em atraso", {}, async () => asResult(await call("GET", "/finance/overview")));
|
|
113
|
+
/* ── Meta Ads ──
|
|
114
|
+
Fluxo pensado para rodar junto com o MCP oficial da Meta no mesmo
|
|
115
|
+
cliente: o assistente lê as métricas das campanhas pelo MCP da Meta
|
|
116
|
+
e grava aqui, no dashboard do cliente certo. Gasto é manual (a API
|
|
117
|
+
da Meta não compartilha), preenchido na aba Meta Ads do MintBoard. */
|
|
118
|
+
server.tool("meta_list_ad_accounts", "Lista as contas de anúncio Meta Ads cadastradas, com o cliente de cada uma", {}, async () => asResult(await call("GET", "/meta-ads/accounts")));
|
|
119
|
+
server.tool("meta_import_metrics", "Importa métricas diárias de campanhas para uma conta de anúncios do MintBoard (upsert por data+campanha). Use depois de ler os insights pelo MCP oficial da Meta.", {
|
|
120
|
+
account_id: z.string().uuid().describe("ID da conta no MintBoard (use meta_list_ad_accounts)"),
|
|
121
|
+
items: z
|
|
122
|
+
.array(z.object({
|
|
123
|
+
date: z.string().describe("ISO date do dia, ex: 2026-06-10"),
|
|
124
|
+
campaign: z.string().optional(),
|
|
125
|
+
impressions: z.number().int().optional(),
|
|
126
|
+
clicks: z.number().int().optional(),
|
|
127
|
+
leads: z.number().int().optional(),
|
|
128
|
+
reach: z.number().int().optional(),
|
|
129
|
+
}))
|
|
130
|
+
.max(500),
|
|
131
|
+
}, async ({ account_id, items }) => asResult(await call("POST", `/meta-ads/accounts/${account_id}/metrics`, { items })));
|
|
132
|
+
server.tool("meta_account_overview", "Dashboard de uma conta de anúncios: totais (impressões, cliques, leads, gasto, CTR, CPL) e série diária", {
|
|
133
|
+
account_id: z.string().uuid(),
|
|
134
|
+
days: z.number().int().min(1).max(180).optional(),
|
|
135
|
+
}, async ({ account_id, days }) => asResult(await call("GET", `/meta-ads/accounts/${account_id}/overview${days ? `?days=${days}` : ""}`)));
|
|
136
|
+
/* ── IA ── */
|
|
137
|
+
server.tool("project_summary", "Gera (via IA do MintBoard) um resumo executivo do projeto pronto para enviar ao cliente", { project_id: z.string().uuid() }, async ({ project_id }) => asResult(await call("POST", `/ai/projects/${project_id}/summary`)));
|
|
138
|
+
const transport = new StdioServerTransport();
|
|
139
|
+
await server.connect(transport);
|
|
140
|
+
console.error(`mintboard-mcp conectado — API: ${API_URL}`);
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mintboard-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Servidor MCP do MintBoard — conecta o CRM a Claude, Gemini e outros assistentes via Model Context Protocol",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "NexMint Labs",
|
|
7
|
+
"homepage": "https://taskflow-lilac-zeta-82.vercel.app",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/MiguelRibasBerlese/CRM-SaaS.git",
|
|
11
|
+
"directory": "packages/mcp-server"
|
|
12
|
+
},
|
|
13
|
+
"keywords": ["mcp", "model-context-protocol", "crm", "mintboard", "claude", "gemini"],
|
|
14
|
+
"type": "module",
|
|
15
|
+
"bin": {
|
|
16
|
+
"mintboard-mcp": "dist/index.js"
|
|
17
|
+
},
|
|
18
|
+
"files": ["dist"],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc",
|
|
21
|
+
"start": "node dist/index.js",
|
|
22
|
+
"prepublishOnly": "npm run build"
|
|
23
|
+
},
|
|
24
|
+
"engines": { "node": ">=20" },
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
27
|
+
"zod": "^3.23.8"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"typescript": "^5.5.0",
|
|
31
|
+
"@types/node": "^22.0.0"
|
|
32
|
+
}
|
|
33
|
+
}
|