elo-skills-mcp 1.0.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/index.js +7 -0
- package/package.json +29 -0
- package/src/brand.js +68 -0
- package/src/license.js +66 -0
- package/src/server.js +69 -0
- package/src/tools/conteudo.js +319 -0
- package/src/tools/estrategia.js +223 -0
- package/src/tools/estrutura.js +341 -0
- package/src/tools/index.js +7 -0
- package/src/tools/marca.js +383 -0
- package/src/tools/setup.js +150 -0
package/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "elo-skills-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Método ELO Criativo — Skills de Estratégia de Conteúdo para Claude",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"elo-skills-mcp": "index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "node index.js",
|
|
12
|
+
"dev": "node --watch index.js"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@modelcontextprotocol/sdk": "^1.10.2"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"mcp",
|
|
19
|
+
"claude",
|
|
20
|
+
"conteudo",
|
|
21
|
+
"estrategia",
|
|
22
|
+
"elo"
|
|
23
|
+
],
|
|
24
|
+
"author": "Ramonnielly Silva Morais",
|
|
25
|
+
"license": "UNLICENSED",
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=18.0.0"
|
|
28
|
+
}
|
|
29
|
+
}
|
package/src/brand.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Leitura e escrita da config de marca da usuária
|
|
2
|
+
// Salva em ~/.elo/[slug].json
|
|
3
|
+
|
|
4
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync } from 'fs'
|
|
5
|
+
import { homedir } from 'os'
|
|
6
|
+
import { join } from 'path'
|
|
7
|
+
|
|
8
|
+
const ELO_DIR = join(homedir(), '.elo')
|
|
9
|
+
|
|
10
|
+
function ensureDir() {
|
|
11
|
+
if (!existsSync(ELO_DIR)) mkdirSync(ELO_DIR, { recursive: true })
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function getBrandConfig(slug = 'minha-marca') {
|
|
15
|
+
ensureDir()
|
|
16
|
+
const path = join(ELO_DIR, `${slug}.json`)
|
|
17
|
+
if (!existsSync(path)) return null
|
|
18
|
+
try {
|
|
19
|
+
return JSON.parse(readFileSync(path, 'utf-8'))
|
|
20
|
+
} catch {
|
|
21
|
+
return null
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function saveBrandConfig(slug = 'minha-marca', config) {
|
|
26
|
+
ensureDir()
|
|
27
|
+
const path = join(ELO_DIR, `${slug}.json`)
|
|
28
|
+
writeFileSync(path, JSON.stringify(config, null, 2), 'utf-8')
|
|
29
|
+
return path
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function listBrands() {
|
|
33
|
+
ensureDir()
|
|
34
|
+
return readdirSync(ELO_DIR)
|
|
35
|
+
.filter(f => f.endsWith('.json'))
|
|
36
|
+
.map(f => f.replace('.json', ''))
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function formatBrandContext(brand) {
|
|
40
|
+
if (!brand) return ''
|
|
41
|
+
|
|
42
|
+
return `
|
|
43
|
+
## IDENTIDADE DE MARCA
|
|
44
|
+
|
|
45
|
+
**Nome/Marca:** ${brand.nome} — ${brand.marca}
|
|
46
|
+
**Nicho:** ${brand.nicho}
|
|
47
|
+
**Posicionamento:** ${brand.posicionamento}
|
|
48
|
+
|
|
49
|
+
**Avatar (cliente ideal):**
|
|
50
|
+
${brand.avatar}
|
|
51
|
+
|
|
52
|
+
**Anti-avatar (quem NÃO é seu cliente):**
|
|
53
|
+
${brand.anti_avatar || 'Não definido'}
|
|
54
|
+
|
|
55
|
+
**Tom de voz:** ${brand.tom_voz}
|
|
56
|
+
|
|
57
|
+
**Palavras que USA:** ${(brand.palavras_usar || []).join(', ')}
|
|
58
|
+
**Palavras que EVITA:** ${(brand.palavras_evitar || []).join(', ')}
|
|
59
|
+
|
|
60
|
+
**Bordões e expressões-marca:**
|
|
61
|
+
${(brand.bordoes || []).map(b => `- "${b}"`).join('\n')}
|
|
62
|
+
|
|
63
|
+
**Arquétipos:** ${(brand.arquetipos || []).join(', ')}
|
|
64
|
+
|
|
65
|
+
**Produto principal:** ${brand.produto || 'Não definido'}
|
|
66
|
+
**Objetivo de negócio:** ${brand.objetivo || 'Não definido'}
|
|
67
|
+
`.trim()
|
|
68
|
+
}
|
package/src/license.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Validação de licença via Supabase
|
|
2
|
+
// Tabela: licenses (key TEXT, active BOOLEAN, expires_at TIMESTAMPTZ, owner_name TEXT)
|
|
3
|
+
|
|
4
|
+
const SUPABASE_URL = 'https://dzicopswqijljhrimtee.supabase.co'
|
|
5
|
+
const SUPABASE_ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImR6aWNvcHN3cWlqbGpocmltdGVlIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzU2NzExNjUsImV4cCI6MjA5MTI0NzE2NX0.JgL9jhJIxqQLm8aeKNLKh8HOS2idGqUOslwAqJUMoaY'
|
|
6
|
+
|
|
7
|
+
let cache = null
|
|
8
|
+
let cacheTime = 0
|
|
9
|
+
const CACHE_TTL = 1000 * 60 * 60 // 1 hora
|
|
10
|
+
|
|
11
|
+
export async function validateLicense() {
|
|
12
|
+
const key = process.env.ELO_LICENSE_KEY
|
|
13
|
+
|
|
14
|
+
if (!key) {
|
|
15
|
+
throw new Error(
|
|
16
|
+
'❌ Licença não encontrada.\n\n' +
|
|
17
|
+
'Adicione sua chave de licença no arquivo de configuração do Claude Code:\n\n' +
|
|
18
|
+
'"ELO_LICENSE_KEY": "SUA-CHAVE-AQUI"\n\n' +
|
|
19
|
+
'Adquira sua licença em: ramonniellymorais.com.br/elo-skills'
|
|
20
|
+
)
|
|
21
|
+
}
|
|
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
|
+
}
|
package/src/server.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
|
|
2
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
3
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js'
|
|
4
|
+
import { validateLicense } from './license.js'
|
|
5
|
+
import * as tools from './tools/index.js'
|
|
6
|
+
|
|
7
|
+
// Lista de todas as ferramentas registradas
|
|
8
|
+
const ALL_TOOLS = Object.values(tools)
|
|
9
|
+
|
|
10
|
+
export async function createServer() {
|
|
11
|
+
const server = new Server(
|
|
12
|
+
{ name: 'elo-skills-mcp', version: '1.0.0' },
|
|
13
|
+
{ capabilities: { tools: {} } }
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
// Lista as ferramentas disponíveis
|
|
17
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
18
|
+
tools: ALL_TOOLS.map(tool => ({
|
|
19
|
+
name: tool.name,
|
|
20
|
+
description: tool.description,
|
|
21
|
+
inputSchema: tool.inputSchema
|
|
22
|
+
}))
|
|
23
|
+
}))
|
|
24
|
+
|
|
25
|
+
// Executa uma ferramenta
|
|
26
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
27
|
+
const { name, arguments: args } = request.params
|
|
28
|
+
|
|
29
|
+
// Valida licença antes de qualquer execução
|
|
30
|
+
// Setup é liberado sem licença para facilitar o onboarding
|
|
31
|
+
const FREE_TOOLS = ['elo_setup']
|
|
32
|
+
if (!FREE_TOOLS.includes(name)) {
|
|
33
|
+
try {
|
|
34
|
+
await validateLicense()
|
|
35
|
+
} catch (err) {
|
|
36
|
+
return {
|
|
37
|
+
content: [{ type: 'text', text: err.message }],
|
|
38
|
+
isError: true
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Encontra e executa a ferramenta
|
|
44
|
+
const tool = ALL_TOOLS.find(t => t.name === name)
|
|
45
|
+
if (!tool) {
|
|
46
|
+
return {
|
|
47
|
+
content: [{ type: 'text', text: `Ferramenta "${name}" não encontrada.` }],
|
|
48
|
+
isError: true
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
return await tool.handler(args || {})
|
|
54
|
+
} catch (err) {
|
|
55
|
+
return {
|
|
56
|
+
content: [{ type: 'text', text: `Erro ao executar ${name}: ${err.message}` }],
|
|
57
|
+
isError: true
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
return server
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export async function startServer() {
|
|
66
|
+
const server = await createServer()
|
|
67
|
+
const transport = new StdioServerTransport()
|
|
68
|
+
await server.connect(transport)
|
|
69
|
+
}
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
// Skills de criação de conteúdo: post, reels, stories, legenda, hooks
|
|
2
|
+
|
|
3
|
+
import { getBrandConfig, formatBrandContext } from '../brand.js'
|
|
4
|
+
|
|
5
|
+
function brandParam() {
|
|
6
|
+
return {
|
|
7
|
+
marca: { type: 'string', description: 'Slug da marca configurada (ex: minha-marca). Se omitido, usa a primeira marca salva.' }
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function resolveContext(marca) {
|
|
12
|
+
const config = getBrandConfig(marca || 'minha-marca')
|
|
13
|
+
if (!config) {
|
|
14
|
+
return `⚠️ Marca "${marca || 'minha-marca'}" não configurada. Use elo_setup primeiro.`
|
|
15
|
+
}
|
|
16
|
+
return formatBrandContext(config)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// ─── POST ────────────────────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
export const postTool = {
|
|
22
|
+
name: 'elo_post',
|
|
23
|
+
description: 'Cria um post completo para Instagram com estrutura ELO. Inclui: gancho, desenvolvimento, CTA, sugestão de formato, etapa C.R.I.A. e KPI.',
|
|
24
|
+
inputSchema: {
|
|
25
|
+
type: 'object',
|
|
26
|
+
properties: {
|
|
27
|
+
tema: { type: 'string', description: 'Tema ou assunto do post' },
|
|
28
|
+
formato: { type: 'string', enum: ['carrossel', 'reels', 'foto', 'livre'], description: 'Formato preferido' },
|
|
29
|
+
cria: { type: 'string', enum: ['C', 'R', 'I', 'A'], description: 'Etapa do C.R.I.A. (Conexão/Repertório/Identidade/Ação)' },
|
|
30
|
+
...brandParam()
|
|
31
|
+
},
|
|
32
|
+
required: ['tema']
|
|
33
|
+
},
|
|
34
|
+
handler: async ({ tema, formato, cria, marca }) => {
|
|
35
|
+
const ctx = resolveContext(marca)
|
|
36
|
+
return {
|
|
37
|
+
content: [{
|
|
38
|
+
type: 'text',
|
|
39
|
+
text: `${ctx}
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
# INSTRUÇÃO — POST INSTAGRAM COM MÉTODO ELO
|
|
44
|
+
|
|
45
|
+
Crie um post completo para Instagram sobre: **"${tema}"**
|
|
46
|
+
${formato ? `Formato preferido: ${formato}` : ''}
|
|
47
|
+
${cria ? `Etapa do C.R.I.A.: ${cria}` : ''}
|
|
48
|
+
|
|
49
|
+
## ESTRUTURA OBRIGATÓRIA
|
|
50
|
+
|
|
51
|
+
### 1. DIAGNÓSTICO ESTRATÉGICO (silencioso — não mostre para a usuária)
|
|
52
|
+
- Qual etapa do C.R.I.A. este post serve?
|
|
53
|
+
- Qual gatilho mental será ativado?
|
|
54
|
+
- Qual nível de consciência do avatar?
|
|
55
|
+
|
|
56
|
+
### 2. GANCHO (primeira linha — máx 1 frase)
|
|
57
|
+
Use a identidade verbal da marca. Provoque, gere identificação ou quebre expectativa.
|
|
58
|
+
NUNCA comece com "Você sabia que..." ou "Hoje vou falar sobre..."
|
|
59
|
+
|
|
60
|
+
### 3. DESENVOLVIMENTO (3 a 5 parágrafos)
|
|
61
|
+
- Alterne frases longas com frases curtíssimas de impacto
|
|
62
|
+
- Inclua pelo menos 1 número, dado ou exemplo real
|
|
63
|
+
- Use o vocabulário da marca
|
|
64
|
+
|
|
65
|
+
### 4. VIRADA
|
|
66
|
+
Momento de reframe ou insight inesperado. A frase mais forte do post.
|
|
67
|
+
|
|
68
|
+
### 5. CTA (convite, não ordem)
|
|
69
|
+
Alinhado com o objetivo de negócio. Nunca forçado.
|
|
70
|
+
|
|
71
|
+
### 6. LEGENDA TÉCNICA
|
|
72
|
+
- Sugestão de formato visual
|
|
73
|
+
- 3 a 5 hashtags estratégicas (não genéricas)
|
|
74
|
+
- Melhor horário de publicação
|
|
75
|
+
|
|
76
|
+
### 7. MÉTRICAS
|
|
77
|
+
- Etapa C.R.I.A.: [C/R/I/A]
|
|
78
|
+
- KPI principal: [alcance / salvamentos / cliques / conversões]
|
|
79
|
+
- Gatilho ativado: [nome do gatilho]`
|
|
80
|
+
}]
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ─── REELS ───────────────────────────────────────────────────────────────────
|
|
86
|
+
|
|
87
|
+
export const reelsTool = {
|
|
88
|
+
name: 'elo_reels',
|
|
89
|
+
description: 'Cria roteiro completo de Reels (30 segundos) com cortes, legenda e análise estratégica.',
|
|
90
|
+
inputSchema: {
|
|
91
|
+
type: 'object',
|
|
92
|
+
properties: {
|
|
93
|
+
tema: { type: 'string', description: 'Tema do Reels' },
|
|
94
|
+
estilo: { type: 'string', enum: ['dialogo_interno', 'quebra_padrao', 'broll_narracao', 'livre'], description: 'Estilo visual' },
|
|
95
|
+
...brandParam()
|
|
96
|
+
},
|
|
97
|
+
required: ['tema']
|
|
98
|
+
},
|
|
99
|
+
handler: async ({ tema, estilo, marca }) => {
|
|
100
|
+
const ctx = resolveContext(marca)
|
|
101
|
+
return {
|
|
102
|
+
content: [{
|
|
103
|
+
type: 'text',
|
|
104
|
+
text: `${ctx}
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
# INSTRUÇÃO — ROTEIRO DE REELS 30s
|
|
109
|
+
|
|
110
|
+
Tema: **"${tema}"**
|
|
111
|
+
${estilo ? `Estilo: ${estilo}` : ''}
|
|
112
|
+
|
|
113
|
+
## ESTRUTURA DO ROTEIRO
|
|
114
|
+
|
|
115
|
+
### GANCHO (0–3s)
|
|
116
|
+
A primeira frase dita ou o texto na tela. Deve parar o scroll. Use a voz da marca.
|
|
117
|
+
|
|
118
|
+
### CORTE 1 (3–8s)
|
|
119
|
+
Desenvolvimento inicial. Problema ou provocação.
|
|
120
|
+
|
|
121
|
+
### CORTE 2 (8–16s)
|
|
122
|
+
Tensão ou dado surpreendente.
|
|
123
|
+
|
|
124
|
+
### CORTE 3 (16–24s)
|
|
125
|
+
Virada / insight principal.
|
|
126
|
+
|
|
127
|
+
### FECHAMENTO (24–30s)
|
|
128
|
+
CTA falado + texto na tela. Simples, direto.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## LEGENDA DO REELS
|
|
133
|
+
(máx 150 caracteres — continua nos comentários se necessário)
|
|
134
|
+
|
|
135
|
+
## TEXTO NA TELA (overlays)
|
|
136
|
+
Liste cada texto que aparece na tela, em ordem.
|
|
137
|
+
|
|
138
|
+
## ANÁLISE ESTRATÉGICA
|
|
139
|
+
- Avatar ativado
|
|
140
|
+
- Etapa C.R.I.A.
|
|
141
|
+
- Gatilho principal
|
|
142
|
+
- KPI esperado: [alcance / compartilhamentos / salvamentos]`
|
|
143
|
+
}]
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ─── STORIES ─────────────────────────────────────────────────────────────────
|
|
149
|
+
|
|
150
|
+
export const storiesTool = {
|
|
151
|
+
name: 'elo_stories',
|
|
152
|
+
description: 'Cria sequência de 5 stories com 5 ângulos diferentes, análise neurológica e score de impacto.',
|
|
153
|
+
inputSchema: {
|
|
154
|
+
type: 'object',
|
|
155
|
+
properties: {
|
|
156
|
+
tema: { type: 'string', description: 'Tema ou objetivo da sequência' },
|
|
157
|
+
objetivo: { type: 'string', enum: ['engajamento', 'autoridade', 'venda', 'bastidor', 'livre'], description: 'Objetivo da sequência' },
|
|
158
|
+
...brandParam()
|
|
159
|
+
},
|
|
160
|
+
required: ['tema']
|
|
161
|
+
},
|
|
162
|
+
handler: async ({ tema, objetivo, marca }) => {
|
|
163
|
+
const ctx = resolveContext(marca)
|
|
164
|
+
return {
|
|
165
|
+
content: [{
|
|
166
|
+
type: 'text',
|
|
167
|
+
text: `${ctx}
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
# INSTRUÇÃO — SEQUÊNCIA DE 5 STORIES
|
|
172
|
+
|
|
173
|
+
Tema: **"${tema}"**
|
|
174
|
+
${objetivo ? `Objetivo: ${objetivo}` : ''}
|
|
175
|
+
|
|
176
|
+
## PARA CADA STORY (1 a 5):
|
|
177
|
+
|
|
178
|
+
**Story [N] — [Nome do ângulo]**
|
|
179
|
+
- **Tipo:** (texto / foto / vídeo / enquete / caixinha)
|
|
180
|
+
- **Ângulo:** (provocação / humor / bastidor / dado / virada)
|
|
181
|
+
- **Texto principal:** (exatamente o que vai escrito)
|
|
182
|
+
- **Elemento interativo:** (enquete, caixinha, contagem, link — se aplicável)
|
|
183
|
+
- **Transição:** como leva para o próximo story
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## OS 5 ÂNGULOS OBRIGATÓRIOS
|
|
188
|
+
1. Provocação / identificação imediata
|
|
189
|
+
2. Dado ou prova inesperada
|
|
190
|
+
3. Bastidor ou história real
|
|
191
|
+
4. Humor ou ironia com sentido
|
|
192
|
+
5. Virada + CTA
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## ANÁLISE NEUROLÓGICA
|
|
197
|
+
Para cada story: qual mecanismo psicológico ativa? (curiosidade, identidade, contraste, pertencimento, urgência)
|
|
198
|
+
|
|
199
|
+
## SCORE DE IMPACTO
|
|
200
|
+
Dê uma nota de 0 a 10 para a sequência em: Retenção / Engajamento / Conversão
|
|
201
|
+
Justifique cada nota em uma linha.`
|
|
202
|
+
}]
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// ─── LEGENDA ─────────────────────────────────────────────────────────────────
|
|
208
|
+
|
|
209
|
+
export const legendaTool = {
|
|
210
|
+
name: 'elo_legenda',
|
|
211
|
+
description: 'Cria ou aprimora a legenda de um post pronto. Adiciona: linha de impacto, expansão, momento aha e CTA com sentido.',
|
|
212
|
+
inputSchema: {
|
|
213
|
+
type: 'object',
|
|
214
|
+
properties: {
|
|
215
|
+
conteudo: { type: 'string', description: 'O post ou conteúdo que precisa de legenda' },
|
|
216
|
+
objetivo: { type: 'string', description: 'Objetivo da legenda: engajar, vender, salvar, compartilhar' },
|
|
217
|
+
...brandParam()
|
|
218
|
+
},
|
|
219
|
+
required: ['conteudo']
|
|
220
|
+
},
|
|
221
|
+
handler: async ({ conteudo, objetivo, marca }) => {
|
|
222
|
+
const ctx = resolveContext(marca)
|
|
223
|
+
return {
|
|
224
|
+
content: [{
|
|
225
|
+
type: 'text',
|
|
226
|
+
text: `${ctx}
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
# INSTRUÇÃO — LEGENDA COM IMPACTO
|
|
231
|
+
|
|
232
|
+
Conteúdo recebido:
|
|
233
|
+
"""
|
|
234
|
+
${conteudo}
|
|
235
|
+
"""
|
|
236
|
+
|
|
237
|
+
${objetivo ? `Objetivo: ${objetivo}` : ''}
|
|
238
|
+
|
|
239
|
+
## ESTRUTURA DA LEGENDA
|
|
240
|
+
|
|
241
|
+
### 1. LINHA DE IMPACTO (primeira linha — máx 1 frase)
|
|
242
|
+
A frase que aparece antes do "ver mais". Deve parar o scroll. Use a voz da marca.
|
|
243
|
+
|
|
244
|
+
### 2. EXPANSÃO (2 a 4 parágrafos)
|
|
245
|
+
Aprofunda o tema. Alterna frases longas com curtas. Inclui 1 dado ou exemplo real.
|
|
246
|
+
|
|
247
|
+
### 3. MOMENTO AHA
|
|
248
|
+
A frase mais forte. O insight que a pessoa vai querer salvar ou copiar.
|
|
249
|
+
|
|
250
|
+
### 4. CTA COM SENTIDO
|
|
251
|
+
Convite genuíno, não ordem. Alinhado ao objetivo de negócio.
|
|
252
|
+
|
|
253
|
+
### 5. HASHTAGS
|
|
254
|
+
5 hashtags: 2 de nicho + 2 de tema + 1 de comunidade. Sem hashtags genéricas (#vida, #amor).`
|
|
255
|
+
}]
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// ─── HOOKS ───────────────────────────────────────────────────────────────────
|
|
261
|
+
|
|
262
|
+
export const hooksTool = {
|
|
263
|
+
name: 'elo_hooks',
|
|
264
|
+
description: 'Gera 20 hooks para um tema. Você escolhe os melhores e recebe 10 refinados com variações.',
|
|
265
|
+
inputSchema: {
|
|
266
|
+
type: 'object',
|
|
267
|
+
properties: {
|
|
268
|
+
tema: { type: 'string', description: 'Tema para gerar hooks' },
|
|
269
|
+
formato: { type: 'string', enum: ['reels', 'carrossel', 'post', 'stories', 'qualquer'], description: 'Formato de destino' },
|
|
270
|
+
...brandParam()
|
|
271
|
+
},
|
|
272
|
+
required: ['tema']
|
|
273
|
+
},
|
|
274
|
+
handler: async ({ tema, formato, marca }) => {
|
|
275
|
+
const ctx = resolveContext(marca)
|
|
276
|
+
return {
|
|
277
|
+
content: [{
|
|
278
|
+
type: 'text',
|
|
279
|
+
text: `${ctx}
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
# INSTRUÇÃO — GERADOR DE HOOKS VIRAIS
|
|
284
|
+
|
|
285
|
+
Tema: **"${tema}"**
|
|
286
|
+
${formato ? `Formato: ${formato}` : ''}
|
|
287
|
+
|
|
288
|
+
## REGRAS DOS HOOKS
|
|
289
|
+
- NUNCA use "descubra" — é fraco e genérico
|
|
290
|
+
- Prefira perguntas desconfortáveis a afirmações seguras
|
|
291
|
+
- Use a voz e os bordões da marca
|
|
292
|
+
- Cada hook deve parar o scroll em 0,3 segundos
|
|
293
|
+
- Varie os padrões: pergunta / afirmação ousada / dado / paradoxo / inversão
|
|
294
|
+
|
|
295
|
+
## FASE 1 — GERAÇÃO
|
|
296
|
+
|
|
297
|
+
Crie 20 hooks para o tema. Organize em 4 categorias de 5:
|
|
298
|
+
|
|
299
|
+
**Pergunta Desconfortável (5 hooks)**
|
|
300
|
+
**Afirmação Polarizante (5 hooks)**
|
|
301
|
+
**Dado ou Número Inesperado (5 hooks)**
|
|
302
|
+
**Inversão ou Paradoxo (5 hooks)**
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
Após apresentar os 20, peça à usuária para escolher os favoritos.
|
|
307
|
+
Com as escolhas, refine os 10 melhores com variações (versão A e B para cada).
|
|
308
|
+
|
|
309
|
+
## FASE 2 — REFINAMENTO (após escolha da usuária)
|
|
310
|
+
|
|
311
|
+
Para cada hook escolhido:
|
|
312
|
+
- **Versão A:** mais provocativa
|
|
313
|
+
- **Versão B:** mais intrigante
|
|
314
|
+
- **Melhor formato de destino:** reels / carrossel / stories
|
|
315
|
+
- **Gatilho ativado:** curiosidade / identidade / contraste / urgência`
|
|
316
|
+
}]
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|