elo-skills-mcp 1.0.0 → 2.0.1
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/LICENSE.md +78 -0
- package/README.md +58 -0
- package/package.json +43 -6
- package/src/api-client.js +90 -0
- package/src/device.js +35 -0
- package/src/license.js +7 -55
- package/src/make-tool.js +32 -0
- package/src/render.js +32 -0
- package/src/server.js +6 -5
- package/src/tools/conteudo.js +18 -260
- package/src/tools/estrategia.js +9 -180
- package/src/tools/estrutura.js +15 -270
- package/src/tools/index.js +1 -0
- package/src/tools/marca.js +12 -324
- package/src/tools/orquestrador.js +33 -0
- package/src/tools/setup.js +1 -1
package/LICENSE.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Licença de Uso — Time ELO (`elo-skills-mcp`)
|
|
2
|
+
|
|
3
|
+
Copyright © 2026 Ramonnielly Silva Morais. Todos os direitos reservados.
|
|
4
|
+
|
|
5
|
+
Este é um software comercial. O pacote `elo-skills-mcp` e o Método ELO Criativo
|
|
6
|
+
que ele acessa são propriedade de Ramonnielly Silva Morais e estão protegidos
|
|
7
|
+
pela Lei nº 9.610/1998 (Direitos Autorais) e pela Lei nº 9.609/1998 (Programa
|
|
8
|
+
de Computador).
|
|
9
|
+
|
|
10
|
+
Adquira uma licença em https://www.elosense.com.br/tools
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 1. Concessão
|
|
15
|
+
|
|
16
|
+
Mediante aquisição de uma chave de licença válida (`ELO_LICENSE_KEY`), a titular
|
|
17
|
+
concede a você uma licença **pessoal, intransferível, não exclusiva e revogável**
|
|
18
|
+
para instalar e executar este pacote, e para usar os resultados por ele gerados,
|
|
19
|
+
inclusive para fins comerciais próprios ou de seus clientes.
|
|
20
|
+
|
|
21
|
+
A licença vigora enquanto a chave estiver ativa e dentro do limite de
|
|
22
|
+
dispositivos contratado.
|
|
23
|
+
|
|
24
|
+
## 2. Restrições
|
|
25
|
+
|
|
26
|
+
Sem autorização prévia e por escrito da titular, você **não pode**:
|
|
27
|
+
|
|
28
|
+
- redistribuir, revender, sublicenciar, alugar, emprestar ou disponibilizar o
|
|
29
|
+
pacote a terceiros, no todo ou em parte;
|
|
30
|
+
- compartilhar, publicar ou transferir sua chave de licença;
|
|
31
|
+
- copiar, extrair, armazenar ou republicar os prompts, templates, rubricas e
|
|
32
|
+
demais conteúdos do Método ELO Criativo servidos pela API;
|
|
33
|
+
- contornar, desabilitar ou interferir na validação de licença, no controle de
|
|
34
|
+
dispositivos ou em qualquer mecanismo técnico de proteção;
|
|
35
|
+
- descompilar, desmontar ou fazer engenharia reversa do pacote ou da API, salvo
|
|
36
|
+
na medida expressamente autorizada por lei;
|
|
37
|
+
- usar o pacote para treinar, ajustar ou avaliar modelos de aprendizado de
|
|
38
|
+
máquina, ou para construir produto ou serviço concorrente;
|
|
39
|
+
- remover ou alterar avisos de autoria, marca ou licença.
|
|
40
|
+
|
|
41
|
+
## 3. Propriedade
|
|
42
|
+
|
|
43
|
+
O software é licenciado, não vendido. Todos os direitos não concedidos
|
|
44
|
+
expressamente permanecem com a titular. O Método ELO Criativo, o C.R.I.A., o
|
|
45
|
+
Efeito-Vórtice e demais denominações e frameworks autorais são de titularidade
|
|
46
|
+
exclusiva de Ramonnielly Silva Morais.
|
|
47
|
+
|
|
48
|
+
O conteúdo que **você** produz com o auxílio das ferramentas é seu.
|
|
49
|
+
|
|
50
|
+
## 4. Dados e funcionamento
|
|
51
|
+
|
|
52
|
+
O pacote valida a licença a cada chamada e registra uso para fins de segurança,
|
|
53
|
+
suporte e controle de dispositivos. Os templates do método não são gravados em
|
|
54
|
+
disco: ficam em memória durante a sessão. Sem validação bem-sucedida em até 72
|
|
55
|
+
horas, a execução é bloqueada.
|
|
56
|
+
|
|
57
|
+
## 5. Vigência e rescisão
|
|
58
|
+
|
|
59
|
+
Esta licença termina automaticamente em caso de descumprimento de qualquer
|
|
60
|
+
cláusula, sem prejuízo das medidas cabíveis. Encerrada a licença, cesse o uso e
|
|
61
|
+
desinstale o pacote.
|
|
62
|
+
|
|
63
|
+
## 6. Garantias e responsabilidade
|
|
64
|
+
|
|
65
|
+
O software é fornecido "no estado em que se encontra", sem garantias de
|
|
66
|
+
adequação a uma finalidade específica ou de operação ininterrupta. Na máxima
|
|
67
|
+
extensão permitida em lei, a responsabilidade da titular fica limitada ao valor
|
|
68
|
+
efetivamente pago pela licença nos 12 meses anteriores ao evento.
|
|
69
|
+
|
|
70
|
+
## 7. Foro
|
|
71
|
+
|
|
72
|
+
Aplica-se a legislação brasileira. Fica eleito o foro da comarca de João
|
|
73
|
+
Pessoa/PB para dirimir controvérsias.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
Dúvidas sobre licenciamento, uso em equipe ou condições especiais:
|
|
78
|
+
**contato@ramonniellymorais.com.br**
|
package/README.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Time ELO — `elo-skills-mcp`
|
|
2
|
+
|
|
3
|
+
**O Método ELO Criativo dentro do seu Claude.** 14 agentes de estratégia de conteúdo — essência, branding, posicionamento, público, ideias, hooks, post, reels, carrossel, stories, legenda, storytelling, formatos, anúncios e análise de performance — escrevendo na sua voz, em português do Brasil.
|
|
4
|
+
|
|
5
|
+
Criado por [Ramonnielly Morais](https://ramonniellymorais.com.br), estrategista de conteúdo e criadora do Método ELO Criativo (Essência · Ligação · Originalidade).
|
|
6
|
+
|
|
7
|
+
> Produto de acesso pago. Requer uma **chave de licença** (`ELO_LICENSE_KEY`).
|
|
8
|
+
> Adquira em **[elosense.com.br/tools](https://www.elosense.com.br/tools)** — R$ 447/ano.
|
|
9
|
+
|
|
10
|
+
## Como usar (após a compra)
|
|
11
|
+
|
|
12
|
+
Você recebe sua chave por e-mail com o passo a passo completo. Em resumo:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install -g elo-skills-mcp
|
|
16
|
+
claude mcp add elo-skills --env ELO_LICENSE_KEY=SUA-CHAVE -- elo-skills-mcp
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Reinicie o Claude Code e rode `elo_setup` para ensinar a sua marca aos agentes. Esse setup alimenta todos os outros — é o que faz a saída sair com a sua cara, e não com cara de IA genérica.
|
|
20
|
+
|
|
21
|
+
Depois é só chamar o agente que resolve o seu problema do momento: `elo_post`, `elo_carrossel`, `elo_reels`, `elo_essencia`, `elo_analise`, e assim por diante.
|
|
22
|
+
|
|
23
|
+
## Os 14 agentes
|
|
24
|
+
|
|
25
|
+
| Frente | Agentes |
|
|
26
|
+
|---|---|
|
|
27
|
+
| **Marca** | essência · branding · posicionamento · público |
|
|
28
|
+
| **Estratégia** | ideias · hooks · análise de performance |
|
|
29
|
+
| **Peça** | post · reels · carrossel · stories · legenda |
|
|
30
|
+
| **Estrutura** | storytelling · formatos |
|
|
31
|
+
|
|
32
|
+
Mais o **Elo do Setup**, que aprende a sua marca e alimenta os 14, e as ferramentas de orquestração (`elo_criar` e `elo_gate`), que produzem em loop fechado e reprovam o que sai genérico.
|
|
33
|
+
|
|
34
|
+
## Requisitos
|
|
35
|
+
|
|
36
|
+
- [Claude Code](https://claude.ai/download)
|
|
37
|
+
- [Node.js](https://nodejs.org) 18+
|
|
38
|
+
- Conexão com a internet (os agentes validam a licença a cada uso)
|
|
39
|
+
|
|
40
|
+
## Como funciona a proteção
|
|
41
|
+
|
|
42
|
+
Este pacote é uma casca fina: traz apenas os metadados das ferramentas. O método
|
|
43
|
+
não viaja no npm — cada agente busca o próprio template numa API autenticada, que
|
|
44
|
+
valida a licença a cada chamada. O conteúdo fica em memória durante a sessão e
|
|
45
|
+
nunca é gravado em disco.
|
|
46
|
+
|
|
47
|
+
## Licença
|
|
48
|
+
|
|
49
|
+
Software comercial. Uso pessoal mediante chave de licença válida. Redistribuição,
|
|
50
|
+
revenda e engenharia reversa não autorizadas. Ver [LICENSE.md](./LICENSE.md).
|
|
51
|
+
|
|
52
|
+
## Suporte
|
|
53
|
+
|
|
54
|
+
Dúvida ou erro? **contato@ramonniellymorais.com.br**
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
© 2026 Ramonnielly Silva Morais — Método ELO Criativo.
|
package/package.json
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "elo-skills-mcp",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
|
+
"description": "Time ELO — 14 agentes de estratégia de conteúdo do Método ELO Criativo, dentro do Claude. Escrevem na sua voz, em português do Brasil.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
8
8
|
"elo-skills-mcp": "index.js"
|
|
9
9
|
},
|
|
10
|
+
"files": [
|
|
11
|
+
"index.js",
|
|
12
|
+
"src/",
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE.md"
|
|
15
|
+
],
|
|
10
16
|
"scripts": {
|
|
11
17
|
"start": "node index.js",
|
|
12
18
|
"dev": "node --watch index.js"
|
|
@@ -16,13 +22,44 @@
|
|
|
16
22
|
},
|
|
17
23
|
"keywords": [
|
|
18
24
|
"mcp",
|
|
25
|
+
"model-context-protocol",
|
|
19
26
|
"claude",
|
|
27
|
+
"claude-code",
|
|
28
|
+
"time-elo",
|
|
29
|
+
"elo",
|
|
30
|
+
"metodo-elo-criativo",
|
|
31
|
+
"agentes",
|
|
32
|
+
"agents",
|
|
33
|
+
"ia",
|
|
34
|
+
"ai",
|
|
35
|
+
"marketing",
|
|
36
|
+
"content-strategy",
|
|
37
|
+
"estrategia-de-conteudo",
|
|
20
38
|
"conteudo",
|
|
21
|
-
"
|
|
22
|
-
"
|
|
39
|
+
"copywriting",
|
|
40
|
+
"brand-voice",
|
|
41
|
+
"voz-de-marca",
|
|
42
|
+
"storytelling",
|
|
43
|
+
"instagram",
|
|
44
|
+
"portugues",
|
|
45
|
+
"pt-br",
|
|
46
|
+
"brasil"
|
|
23
47
|
],
|
|
24
|
-
"author":
|
|
25
|
-
|
|
48
|
+
"author": {
|
|
49
|
+
"name": "Ramonnielly Silva Morais",
|
|
50
|
+
"email": "contato@ramonniellymorais.com.br",
|
|
51
|
+
"url": "https://ramonniellymorais.com.br"
|
|
52
|
+
},
|
|
53
|
+
"license": "SEE LICENSE IN LICENSE.md",
|
|
54
|
+
"homepage": "https://www.elosense.com.br/tools",
|
|
55
|
+
"repository": {
|
|
56
|
+
"type": "git",
|
|
57
|
+
"url": "git+https://github.com/ramonniellymorais/elo-skills-mcp.git"
|
|
58
|
+
},
|
|
59
|
+
"bugs": {
|
|
60
|
+
"url": "https://github.com/ramonniellymorais/elo-skills-mcp/issues",
|
|
61
|
+
"email": "contato@ramonniellymorais.com.br"
|
|
62
|
+
},
|
|
26
63
|
"engines": {
|
|
27
64
|
"node": ">=18.0.0"
|
|
28
65
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// Cliente da API de prompts do Método ELO.
|
|
2
|
+
// O método NÃO viaja no pacote npm: cada tool busca seu template aqui,
|
|
3
|
+
// numa API autenticada que valida a licença server-side.
|
|
4
|
+
//
|
|
5
|
+
// Cache só em memória (some ao fechar o Claude) — nunca grava o método em disco.
|
|
6
|
+
// Fallback offline limitado: usa cache em RAM por até HARD_OFFLINE_LIMIT desde a
|
|
7
|
+
// última validação online bem-sucedida; passado esse limite, bloqueia mesmo offline.
|
|
8
|
+
|
|
9
|
+
import { getDeviceHash, getDeviceLabel } from './device.js'
|
|
10
|
+
|
|
11
|
+
const API_URL = process.env.ELO_API_URL || 'https://elo-skills-mcp.vercel.app'
|
|
12
|
+
const TEMPLATE_TTL = 1000 * 60 * 20 // 20 min de cache em RAM por tool
|
|
13
|
+
const HARD_OFFLINE_LIMIT = 1000 * 60 * 60 * 72 // 72h sem validação online → bloqueia
|
|
14
|
+
|
|
15
|
+
const PURCHASE_URL = 'elosense.com.br/tools'
|
|
16
|
+
const SUPPORT_EMAIL = 'contato@ramonniellymorais.com.br'
|
|
17
|
+
|
|
18
|
+
const templateCache = new Map() // toolName -> { template, ts }
|
|
19
|
+
let lastOnlineOk = 0
|
|
20
|
+
|
|
21
|
+
export async function fetchTemplate(toolName) {
|
|
22
|
+
const key = process.env.ELO_LICENSE_KEY
|
|
23
|
+
if (!key) throw new Error(missingKeyMessage())
|
|
24
|
+
|
|
25
|
+
const now = Date.now()
|
|
26
|
+
const cached = templateCache.get(toolName)
|
|
27
|
+
if (cached && now - cached.ts < TEMPLATE_TTL) return cached.template
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
const res = await fetch(`${API_URL}/api/tool`, {
|
|
31
|
+
method: 'POST',
|
|
32
|
+
headers: {
|
|
33
|
+
'Content-Type': 'application/json',
|
|
34
|
+
'Authorization': `Bearer ${key}`
|
|
35
|
+
},
|
|
36
|
+
body: JSON.stringify({
|
|
37
|
+
tool: toolName,
|
|
38
|
+
device_hash: getDeviceHash(key),
|
|
39
|
+
device_label: getDeviceLabel()
|
|
40
|
+
})
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
if (res.status === 401 || res.status === 403) {
|
|
44
|
+
const { reason } = await safeJson(res)
|
|
45
|
+
throw new Error(licenseError(reason))
|
|
46
|
+
}
|
|
47
|
+
if (res.status === 429) {
|
|
48
|
+
throw new Error('❌ Muitas requisições em pouco tempo. Aguarde alguns minutos e tente de novo.')
|
|
49
|
+
}
|
|
50
|
+
if (!res.ok) throw new Error(`Erro ${res.status} ao buscar a skill.`)
|
|
51
|
+
|
|
52
|
+
const data = await safeJson(res)
|
|
53
|
+
if (!data.template) throw new Error('Resposta inválida do servidor.')
|
|
54
|
+
|
|
55
|
+
templateCache.set(toolName, { template: data.template, ts: now })
|
|
56
|
+
lastOnlineOk = now
|
|
57
|
+
return data.template
|
|
58
|
+
|
|
59
|
+
} catch (err) {
|
|
60
|
+
if (err.message?.startsWith('❌')) throw err
|
|
61
|
+
// Falha de rede: usa cache em RAM se ainda dentro da janela offline
|
|
62
|
+
if (cached && now - lastOnlineOk < HARD_OFFLINE_LIMIT) return cached.template
|
|
63
|
+
throw new Error('❌ Sem conexão com o servidor das skills ELO. Verifique sua internet e tente de novo.')
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function missingKeyMessage() {
|
|
68
|
+
return (
|
|
69
|
+
'❌ Licença não encontrada.\n\n' +
|
|
70
|
+
'Adicione sua chave de licença no arquivo de configuração do Claude Code:\n\n' +
|
|
71
|
+
'"ELO_LICENSE_KEY": "SUA-CHAVE-AQUI"\n\n' +
|
|
72
|
+
`Adquira sua licença em: ${PURCHASE_URL}`
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function licenseError(reason) {
|
|
77
|
+
switch (reason) {
|
|
78
|
+
case 'not_found': return `❌ Chave de licença inválida. Verifique a chave ou adquira em ${PURCHASE_URL}`
|
|
79
|
+
case 'revoked': return `❌ Sua licença foi revogada. Entre em contato: ${SUPPORT_EMAIL}`
|
|
80
|
+
case 'refunded': return '❌ Esta licença foi reembolsada e está inativa.'
|
|
81
|
+
case 'expired': return `❌ Sua licença expirou. Renove em ${PURCHASE_URL}`
|
|
82
|
+
case 'suspended': return `❌ Sua licença está suspensa. Entre em contato: ${SUPPORT_EMAIL}`
|
|
83
|
+
case 'too_many_devices': return `❌ Limite de dispositivos atingido para esta licença. Entre em contato: ${SUPPORT_EMAIL}`
|
|
84
|
+
default: return `❌ Não foi possível validar sua licença. Adquira ou renove em ${PURCHASE_URL}`
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function safeJson(res) {
|
|
89
|
+
try { return await res.json() } catch { return {} }
|
|
90
|
+
}
|
package/src/device.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Identidade de dispositivo para binding de licença.
|
|
2
|
+
// Gera um id estável por máquina (guardado em ~/.elo/device.json) e
|
|
3
|
+
// só envia ao servidor o HASH combinado com a chave — nunca o id cru.
|
|
4
|
+
|
|
5
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs'
|
|
6
|
+
import { homedir, hostname } from 'os'
|
|
7
|
+
import { join } from 'path'
|
|
8
|
+
import { createHash, randomUUID } from 'crypto'
|
|
9
|
+
|
|
10
|
+
const ELO_DIR = join(homedir(), '.elo')
|
|
11
|
+
const DEVICE_PATH = join(ELO_DIR, 'device.json')
|
|
12
|
+
|
|
13
|
+
function getDeviceId() {
|
|
14
|
+
if (!existsSync(ELO_DIR)) mkdirSync(ELO_DIR, { recursive: true })
|
|
15
|
+
if (existsSync(DEVICE_PATH)) {
|
|
16
|
+
try {
|
|
17
|
+
const saved = JSON.parse(readFileSync(DEVICE_PATH, 'utf-8'))
|
|
18
|
+
if (saved.id) return saved.id
|
|
19
|
+
} catch { /* recria abaixo */ }
|
|
20
|
+
}
|
|
21
|
+
const id = randomUUID()
|
|
22
|
+
writeFileSync(DEVICE_PATH, JSON.stringify({ id, created_at: new Date().toISOString() }, null, 2))
|
|
23
|
+
return id
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Hash estável do dispositivo combinado com a chave. O servidor limita
|
|
27
|
+
// quantos hashes distintos uma licença pode ter (max_activations).
|
|
28
|
+
export function getDeviceHash(licenseKey) {
|
|
29
|
+
return createHash('sha256').update(`${getDeviceId()}:${licenseKey}`).digest('hex')
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Rótulo amigável só para suporte ("MacBook da Ramon"). Não identifica de forma única.
|
|
33
|
+
export function getDeviceLabel() {
|
|
34
|
+
try { return hostname() } catch { return 'unknown' }
|
|
35
|
+
}
|
package/src/license.js
CHANGED
|
@@ -1,66 +1,18 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
1
|
+
// Checagem local mínima: só confirma que a chave está presente.
|
|
2
|
+
// A validação REAL (ativa / expirada / revogada / dispositivos) acontece
|
|
3
|
+
// server-side, na API de prompts, a cada chamada de tool. Nenhuma URL ou
|
|
4
|
+
// chave de Supabase vive mais no pacote.
|
|
3
5
|
|
|
4
|
-
const
|
|
5
|
-
const SUPABASE_ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImR6aWNvcHN3cWlqbGpocmltdGVlIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzU2NzExNjUsImV4cCI6MjA5MTI0NzE2NX0.JgL9jhJIxqQLm8aeKNLKh8HOS2idGqUOslwAqJUMoaY'
|
|
6
|
+
const PURCHASE_URL = 'elosense.com.br/tools'
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
let cacheTime = 0
|
|
9
|
-
const CACHE_TTL = 1000 * 60 * 60 // 1 hora
|
|
10
|
-
|
|
11
|
-
export async function validateLicense() {
|
|
8
|
+
export function assertLicensePresent() {
|
|
12
9
|
const key = process.env.ELO_LICENSE_KEY
|
|
13
|
-
|
|
14
10
|
if (!key) {
|
|
15
11
|
throw new Error(
|
|
16
12
|
'❌ Licença não encontrada.\n\n' +
|
|
17
13
|
'Adicione sua chave de licença no arquivo de configuração do Claude Code:\n\n' +
|
|
18
14
|
'"ELO_LICENSE_KEY": "SUA-CHAVE-AQUI"\n\n' +
|
|
19
|
-
|
|
15
|
+
`Adquira sua licença em: ${PURCHASE_URL}`
|
|
20
16
|
)
|
|
21
17
|
}
|
|
22
|
-
|
|
23
|
-
// Cache válido — não bate no Supabase a cada chamada
|
|
24
|
-
const now = Date.now()
|
|
25
|
-
if (cache && now - cacheTime < CACHE_TTL) return cache
|
|
26
|
-
|
|
27
|
-
try {
|
|
28
|
-
const res = await fetch(
|
|
29
|
-
`${SUPABASE_URL}/rest/v1/licenses?key=eq.${encodeURIComponent(key)}&select=active,expires_at,owner_name`,
|
|
30
|
-
{
|
|
31
|
-
headers: {
|
|
32
|
-
'apikey': SUPABASE_ANON_KEY,
|
|
33
|
-
'Authorization': `Bearer ${SUPABASE_ANON_KEY}`
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
if (!res.ok) throw new Error('Erro ao verificar licença.')
|
|
39
|
-
|
|
40
|
-
const data = await res.json()
|
|
41
|
-
|
|
42
|
-
if (!data.length) {
|
|
43
|
-
throw new Error('❌ Chave de licença inválida. Verifique a chave ou adquira em ramonniellymorais.com.br/elo-skills')
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const license = data[0]
|
|
47
|
-
|
|
48
|
-
if (!license.active) {
|
|
49
|
-
throw new Error('❌ Sua licença está desativada. Entre em contato: contato@ramonniellymorais.com.br')
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (license.expires_at && new Date(license.expires_at) < new Date()) {
|
|
53
|
-
throw new Error('❌ Sua licença expirou. Renove em ramonniellymorais.com.br/elo-skills')
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
cache = license
|
|
57
|
-
cacheTime = now
|
|
58
|
-
return license
|
|
59
|
-
|
|
60
|
-
} catch (err) {
|
|
61
|
-
if (err.message.startsWith('❌')) throw err
|
|
62
|
-
// Fallback offline: se Supabase estiver fora, usa cache anterior
|
|
63
|
-
if (cache) return cache
|
|
64
|
-
throw new Error('❌ Não foi possível verificar a licença. Verifique sua conexão.')
|
|
65
|
-
}
|
|
66
18
|
}
|
package/src/make-tool.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Fábrica de tools "finas". Os metadados (name/description/inputSchema) ficam
|
|
2
|
+
// locais e públicos — o MCP precisa deles para listar. O TEMPLATE do método é
|
|
3
|
+
// buscado na API autenticada em runtime e renderizado em memória com o contexto
|
|
4
|
+
// de marca local (que nunca vai ao servidor).
|
|
5
|
+
|
|
6
|
+
import { getBrandConfig, formatBrandContext } from './brand.js'
|
|
7
|
+
import { fetchTemplate } from './api-client.js'
|
|
8
|
+
import { renderTemplate } from './render.js'
|
|
9
|
+
|
|
10
|
+
function resolveContext(marca) {
|
|
11
|
+
const config = getBrandConfig(marca || 'minha-marca')
|
|
12
|
+
if (!config) {
|
|
13
|
+
return `⚠️ Marca "${marca || 'minha-marca'}" não configurada. Use elo_setup primeiro.`
|
|
14
|
+
}
|
|
15
|
+
return formatBrandContext(config)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function makeTool({ name, description, inputSchema, prepare }) {
|
|
19
|
+
return {
|
|
20
|
+
name,
|
|
21
|
+
description,
|
|
22
|
+
inputSchema,
|
|
23
|
+
handler: async (args = {}) => {
|
|
24
|
+
const { marca, ...params } = args
|
|
25
|
+
const ctx = resolveContext(marca)
|
|
26
|
+
const data = prepare ? prepare(params) : params
|
|
27
|
+
const template = await fetchTemplate(name) // valida licença server-side; lança erro amigável
|
|
28
|
+
const body = renderTemplate(template, data)
|
|
29
|
+
return { content: [{ type: 'text', text: `${ctx}\n\n---\n\n${body}` }] }
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
package/src/render.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Renderizador leve estilo mustache para os templates do método.
|
|
2
|
+
// Suporta:
|
|
3
|
+
// {{var}} → substitui pelo valor (vazio se ausente)
|
|
4
|
+
// {{#var}}...{{/var}} → inclui o bloco só se var for "verdadeiro"
|
|
5
|
+
// {{^var}}...{{/var}} → inclui o bloco só se var for "falso" (else)
|
|
6
|
+
//
|
|
7
|
+
// Os templates vêm da API autenticada; o cliente injeta os params aqui,
|
|
8
|
+
// em memória, e nunca grava o método em disco.
|
|
9
|
+
|
|
10
|
+
function isTruthy(v) {
|
|
11
|
+
if (Array.isArray(v)) return v.length > 0
|
|
12
|
+
return v !== undefined && v !== null && v !== '' && v !== false && v !== 0
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function renderTemplate(template, data = {}) {
|
|
16
|
+
// Seções (normais e invertidas) — resolvidas primeiro
|
|
17
|
+
let out = template.replace(
|
|
18
|
+
/\{\{([#^])(\w+)\}\}([\s\S]*?)\{\{\/\2\}\}/g,
|
|
19
|
+
(_, kind, key, inner) => {
|
|
20
|
+
const show = kind === '#' ? isTruthy(data[key]) : !isTruthy(data[key])
|
|
21
|
+
return show ? inner : ''
|
|
22
|
+
}
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
// Variáveis simples
|
|
26
|
+
out = out.replace(/\{\{(\w+)\}\}/g, (_, key) => {
|
|
27
|
+
const v = data[key]
|
|
28
|
+
return v === undefined || v === null ? '' : String(v)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
return out
|
|
32
|
+
}
|
package/src/server.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
|
|
2
2
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
3
3
|
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js'
|
|
4
|
-
import {
|
|
4
|
+
import { assertLicensePresent } from './license.js'
|
|
5
5
|
import * as tools from './tools/index.js'
|
|
6
6
|
|
|
7
7
|
// Lista de todas as ferramentas registradas
|
|
@@ -9,7 +9,7 @@ const ALL_TOOLS = Object.values(tools)
|
|
|
9
9
|
|
|
10
10
|
export async function createServer() {
|
|
11
11
|
const server = new Server(
|
|
12
|
-
{ name: 'elo-skills-mcp', version: '
|
|
12
|
+
{ name: 'elo-skills-mcp', version: '2.0.0' },
|
|
13
13
|
{ capabilities: { tools: {} } }
|
|
14
14
|
)
|
|
15
15
|
|
|
@@ -26,12 +26,13 @@ export async function createServer() {
|
|
|
26
26
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
27
27
|
const { name, arguments: args } = request.params
|
|
28
28
|
|
|
29
|
-
//
|
|
30
|
-
//
|
|
29
|
+
// Checagem local de presença da chave antes de qualquer execução.
|
|
30
|
+
// A validação real (ativa/expirada/revogada) é server-side, na API de prompts.
|
|
31
|
+
// Setup é liberado sem licença para facilitar o onboarding.
|
|
31
32
|
const FREE_TOOLS = ['elo_setup']
|
|
32
33
|
if (!FREE_TOOLS.includes(name)) {
|
|
33
34
|
try {
|
|
34
|
-
|
|
35
|
+
assertLicensePresent()
|
|
35
36
|
} catch (err) {
|
|
36
37
|
return {
|
|
37
38
|
content: [{ type: 'text', text: err.message }],
|