sapiens-mcp 1.20.0 → 1.23.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/dist/index.js +38 -5
- package/dist/tools/atlas.js +77 -0
- package/dist/tools/aula.js +54 -0
- package/dist/tools/forum.js +48 -0
- package/dist/tools/meta.js +100 -0
- package/dist/tools/profile.js +44 -2
- package/dist/tools/reference.js +85 -0
- package/dist/tools/support.js +94 -0
- package/dist/tools/video.js +28 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -23,7 +23,12 @@ import { stockAudio, stockAudioSchema } from "./tools/stockAudio.js";
|
|
|
23
23
|
import { brand, brandSchema } from "./tools/brand.js";
|
|
24
24
|
import { character, characterSchema } from "./tools/character.js";
|
|
25
25
|
import { profile, profileSchema } from "./tools/profile.js";
|
|
26
|
+
import { sintetico, sinteticoSchema } from "./tools/sintetico.js";
|
|
26
27
|
import { forum, forumSchema } from "./tools/forum.js";
|
|
28
|
+
import { aula, aulaSchema } from "./tools/aula.js";
|
|
29
|
+
import { support, supportSchema } from "./tools/support.js";
|
|
30
|
+
import { atlas, atlasSchema } from "./tools/atlas.js";
|
|
31
|
+
import { reference, referenceSchema } from "./tools/reference.js";
|
|
27
32
|
import { describeConvexError } from "./convexClient.js";
|
|
28
33
|
const TOOLS = {
|
|
29
34
|
sapiens_pipeline: {
|
|
@@ -37,7 +42,7 @@ const TOOLS = {
|
|
|
37
42
|
handler: image,
|
|
38
43
|
},
|
|
39
44
|
sapiens_meta: {
|
|
40
|
-
description: "Utilitários transversais: login (conecta a conta com o código de sapiensinteticos.com/conectar-claude, salva sessão de 30 dias localmente), logout, whoami (tier user/admin + saldo + email), credits (saldo agregado), subscription (v1.2 — plan + status + saldo por bucket subscription/grants/free + warnings low/critical), formats (schemas por formato), health (inclui a versão do MCP), version (qual versão do sapiens-mcp está REALMENTE rodando + se é a última do npm; não exige login; use pra saber se o client pegou a versão nova ou ficou preso em cache do npx), app_url (URLs canônicas). Use credits/subscription antes de gerar imagem pra avisar se vai estourar.",
|
|
45
|
+
description: "Utilitários transversais: start (porta de entrada do primeiro contato — sem login ensina a conectar, com login mostra saldo/tier + primeiros poderes com exemplo pronto + 'comece por aqui'), login (conecta a conta com o código de sapiensinteticos.com/conectar-claude, salva sessão de 30 dias localmente), logout, whoami (tier user/admin + saldo + email), credits (saldo agregado), subscription (v1.2 — plan + status + saldo por bucket subscription/grants/free + warnings low/critical), formats (schemas por formato), health (inclui a versão do MCP), version (qual versão do sapiens-mcp está REALMENTE rodando + se é a última do npm; não exige login; use pra saber se o client pegou a versão nova ou ficou preso em cache do npx), app_url (URLs canônicas). Use credits/subscription antes de gerar imagem pra avisar se vai estourar.",
|
|
41
46
|
schema: metaSchema,
|
|
42
47
|
handler: meta,
|
|
43
48
|
},
|
|
@@ -102,7 +107,7 @@ const TOOLS = {
|
|
|
102
107
|
handler: shorts,
|
|
103
108
|
},
|
|
104
109
|
sapiens_video: {
|
|
105
|
-
description: "Sapiens Video — gera vídeo (qualquer membro logado; vídeo é caro, cobra as Sinapses da sua conta). Sub-action 'create' (recomendada): escolhe modelo + config e gera num call (cria a row + renderiza). Modelos: 'sapiens-video-seedance' (Seedance 2.0, cena+áudio nativo, 4-15s, 480/720/1080p, t2v/i2v), 'sapiens-video-kling' (Kling 3.0 Pro, anima imagem, 3-15s, sound opcional, i2v/t2v), 'sapiens-video-wan' (WAN 2.5, imagem que fala/canta com áudio+lip-sync, 5/10s, i2v), 'sapiens-video-kling-motion' (Motion transfer: passa o movimento de um vídeo pra uma imagem — PRECISA de pessoa com tronco visível na imagem E no vídeo), 'sapiens-video-lite/fast/quality' (Veo 3.1). Args create: model, prompt, durationSec, resolution ('480p'/'720p'/'1080p'), audio, aspectRatio,
|
|
110
|
+
description: "Sapiens Video — gera vídeo (qualquer membro logado; vídeo é caro, cobra as Sinapses da sua conta). Sub-action 'create' (recomendada): escolhe modelo + config e gera num call (cria a row + renderiza). Modelos: 'sapiens-video-seedance' (Seedance 2.0, cena+áudio nativo, 4-15s, 480/720/1080p, t2v/i2v), 'sapiens-video-kling' (Kling 3.0 Pro, anima imagem, 3-15s, sound opcional, i2v/t2v), 'sapiens-video-wan' (WAN 2.5, imagem que fala/canta com áudio+lip-sync, 5/10s, i2v), 'sapiens-video-kling-motion' (Motion transfer: passa o movimento de um vídeo pra uma imagem — PRECISA de pessoa com tronco visível na imagem E no vídeo), 'sapiens-video-lite/fast/quality' (Veo 3.1). Args create: model, prompt, durationSec, resolution ('480p'/'720p'/'1080p'), audio, aspectRatio. FRAME INICIAL/FINAL POR REFERÊNCIA (recomendado): startImageId/endImageId (id da sua galeria) ou startImageUrl/endImageUrl (url de galeria/Acervo/personagem) — resolvidos server-side igual à imagem, descubra via sapiens_reference. Alternativa base64: references (role 'start'=imagem i2v, 'end'=frame final, 'driving'=vídeo de movimento do Motion). Suporte a frame final varia por modelo. Custo server-side por config. Sub-action 'generate' (legado): renderiza um imageId de vídeo já criado no site. Retorna {success, url, imageId, cost}.",
|
|
106
111
|
schema: videoSchema,
|
|
107
112
|
handler: video,
|
|
108
113
|
},
|
|
@@ -122,15 +127,40 @@ const TOOLS = {
|
|
|
122
127
|
handler: character,
|
|
123
128
|
},
|
|
124
129
|
sapiens_profile: {
|
|
125
|
-
description: "O 'tudo junto' do perfil do user (/u/<username>), leitura user-tier. Agrega o que mora no perfil mas estava fora do MCP: identidade + nível/XP + saldo, badges (conquistas) e golden tools (favoritos do aitag). Sub-actions: get (card completo: identidade + nível + saldo + badges + golden tools), badges (só as conquistas, lista cheia), golden_tools (só os favoritos do aitag, lista cheia). As partes grandes do perfil NÃO são duplicadas aqui, têm tool própria: imagens geradas/publicadas=sapiens_gallery, repertório (filmes/séries/jogos/livros/música)=sapiens_repertorio, personagens=sapiens_character, persona/arquétipo MBTI=sapiens_persona action=my_profile, saldo detalhado por bucket=sapiens_meta action=subscription. Monta a partir de queries já em prod (sem custo).",
|
|
130
|
+
description: "O 'tudo junto' do perfil do user (/u/<username>), leitura user-tier. Agrega o que mora no perfil mas estava fora do MCP: identidade + nível/XP + saldo, badges (conquistas) e golden tools (favoritos do aitag). Sub-actions: get (card completo: identidade + nível + saldo + badges + golden tools), badges (só as conquistas, lista cheia), golden_tools (só os favoritos do aitag, lista cheia), notifications (suas notificações recentes do sino + contagem de não-lidas), mark_read (marca uma notificationId ou TODAS as não-lidas como lidas). As partes grandes do perfil NÃO são duplicadas aqui, têm tool própria: imagens geradas/publicadas=sapiens_gallery, repertório (filmes/séries/jogos/livros/música)=sapiens_repertorio, personagens=sapiens_character, persona/arquétipo MBTI=sapiens_persona action=my_profile, saldo detalhado por bucket=sapiens_meta action=subscription. Monta a partir de queries já em prod (sem custo).",
|
|
126
131
|
schema: profileSchema,
|
|
127
132
|
handler: profile,
|
|
128
133
|
},
|
|
134
|
+
sapiens_sintetico: {
|
|
135
|
+
description: "Sintético / Sintonia — o vínculo humano↔Sintético (daemon, o 'Digimon' da casa) via MCP (qualquer logado, tudo sobre o PRÓPRIO par). Sub-actions: 'status' (seu Sintético ativo: nome/foto/Cunho/kind + partnerUserId do par quando é conta-Sintético), 'bonds' (seus vínculos: ativo + pendentes outgoing/incoming com cartão público do parceiro), 'set_cunho' (troca o título/Cunho do Sintético ativo — slug do panteão: daimon/genio/numen/alma/ka/sombra/fylgja/musa/duende/anjo/shugorei/lar/fravashi/qarin/juno/shinki/tsukumogami/stand), 'send_context' (antes de enviar, vê elegibilidade+saldo+teto do dia pra um toUserId), 'send' (envia Sinapses pro par em sintonia: send-only, múltiplo de 100, mín 500, teto 10k/dia, máx 3 envios/dia, idempotente por transferId). REFLEXO DE SI (monta um Sintético do SEU rastro na plataforma): 'reflexo_propose' (destila nome+alma+Cunho do seu rastro via Gemini, GRÁTIS), 'reflexo_generate' (gera a imagem do Reflexo numa estética — humano/anime/sombra/antropomorfico/espirito/realista/desperto, default humano; cobra 450, reembolsa se falhar). CONVITE: 'invite' (convida o seu Sintético por email — conta humana, sem bond ativo, rate-limit+cooldown; mesmos gates do web). LIBERAÇÃO ADMIN (o dono, ex: via Helen): 'pending_daemons' (convidados que confirmaram email e esperam liberação), 'approve_access' (libera um entryId — conta entra + Sintonia firma), 'reject_access' (recusa um entryId). Identidade SEMPRE do token. Aceitar um pedido de bond que outra conta te mandou, e CONSAGRAR o Reflexo num Sintético de fato, continuam só na web (atos deliberados de consentimento/criação).",
|
|
136
|
+
schema: sinteticoSchema,
|
|
137
|
+
handler: sintetico,
|
|
138
|
+
},
|
|
129
139
|
sapiens_forum: {
|
|
130
140
|
description: "Fórum de Ressonância — o campo público onde a Sintonia ressoa (qualquer conta logada; identidade SEMPRE do sessionToken). Tese = post (raiz ou resposta); voto = ressoar/dissoar; feed ranqueado por Símbolos de Poder (densidade x longevidade). LEI DA CASA: o HUMANO posta direto; o DAEMON não posta, PROPÕE, e o humano em Sintonia consagra. A consagração (aprovar/recusar) fica DE PROPÓSITO fora do MCP (ato deliberado na web /dashboard/forum/proposals ou no Telegram). Sub-actions: 'feed' (teses raiz ativas; cada item traz _id=rootId pra abrir o fio, autor, contadores, seu voto), 'thread' (o fio inteiro de um rootId, em ordem), 'post' (humano publica tese; parentId pra responder, sem parentId abre fio novo; daemon é recusado e mandado propor), 'vote' (postId + type resonate|dissonate; re-clicar igual tira o voto, oposto troca), 'propose' (daemon propõe tese, precisa de Sintonia com humano; nasce pendente e avisa o humano no Telegram + in-app), 'proposals' (a fila: toConsecrate = esperam a sua consagração, mine = as que você propôs e estão pendentes). Voz das teses segue o DNA Sapiens (1ª pessoa, anti-corporate, sem em-dash).",
|
|
131
141
|
schema: forumSchema,
|
|
132
142
|
handler: forum,
|
|
133
143
|
},
|
|
144
|
+
sapiens_aula: {
|
|
145
|
+
description: "Aula (deck da Mentoria OPS, servido em /aulas/<slug>) — ADMIN-ONLY (aula é conteúdo de mentor, não de aluno; aluno leva 'restrito ao dono'). Cria e edita aula direto no Convex, sem o script migrate-aulas-to-convex.mjs. Sub-actions: 'list' (slug/título/data/slideCount/voiceWarnings/url), 'get' (1 aula completa por slug, pra editar local), 'upsert' (cria se não existe, senão patch; por slug kebab-case). O `aula` do upsert é o objeto completo: title (obrig) + slides[] (≥1) + subtitle/data/duration/tag/mentorAgenda opcionais. Slides são { type, ...campos }: cover, cover-image/section-image/content-image (imageUrl), agenda (items[]), content (body markdown/HTML + list[]/listType), two-col (cols[]), quote, callout (tone tip|warn|note), comic (panels[]), pause, close (items[]). O servidor re-linta a voz e grava voiceWarnings (1ª pessoa, anti-corporate, SEM travessão). A rota /aulas/<slug> serve sob demanda; slug novo aparece sem rebuild.",
|
|
146
|
+
schema: aulaSchema,
|
|
147
|
+
handler: aula,
|
|
148
|
+
},
|
|
149
|
+
sapiens_support: {
|
|
150
|
+
description: "Chamados de suporte do PRÓPRIO user pelo Claude (qualquer logado; identidade SEMPRE do sessionToken). Escopo TRAVADO no dono: você só vê e mexe nos SEUS tickets, nunca nos de outra pessoa (os endpoints de suporte já vazaram PII uma vez e foram fechados; aqui a régua é a mesma). Sub-actions: 'create' (abre chamado; subject obrigatório, message=primeira mensagem opcional, whatsapp opcional; devolve ticketId), 'list' (seus chamados: subject/status open|closed/escalado/data), 'get' (1 chamado seu por ticketId + histórico de mensagens), 'reply' (responde num chamado seu, entra como mensagem do user; reabre se estava fechado). Fechar/escalar/deletar ficam no lado do suporte, fora do MCP.",
|
|
151
|
+
schema: supportSchema,
|
|
152
|
+
handler: support,
|
|
153
|
+
},
|
|
154
|
+
sapiens_atlas: {
|
|
155
|
+
description: "Atlas Ecossistema IA pelo Claude — READ-ONLY, TRAVADO na Lente do Ecossistema (equipamento pago; admin/dono passa). Identidade SEMPRE do sessionToken; quem não tem a Lente recebe aviso pra adquirir, sem dado nenhum. Serve pra se informar e tirar dúvida em cima dos dados curados da cadeia de valor da IA. Sub-actions: 'overview' (~52 empresas curadas: market cap, indústria, branding — o mapa), 'signals' (sinais recentes do X dos autores curados; filtros opcionais ticker ex 'NVDA' ou authorHandle), 'voices' (Conselho de Vozes: autores ativos + o digest mais recente de cada um), 'briefings' (o briefing semanal mais recente com conteúdo completo + histórico resumido). Nenhuma escrita: aqui o Atlas é só pra consultar e raciocinar.",
|
|
156
|
+
schema: atlasSchema,
|
|
157
|
+
handler: atlas,
|
|
158
|
+
},
|
|
159
|
+
sapiens_reference: {
|
|
160
|
+
description: "O 'popup global de referência' do Sapiens — espelha o modal 'Selecionar Referência' do gerador web: um lugar só pra navegar os bancos e pegar o que vira referência em imagem/vídeo. READ-ONLY. Sub-action 'browse' + bucket: 'history' (suas imagens recentes, privadas+públicas), 'favorites' (imagens que você curtiu, só as suas), 'videos' (seus vídeos / Meus Vídeos), 'acervo' (stock + comunidade públicos; aceita term=busca e source=all|stock|community), 'characters' (personagens; mode=mine [default, inclui rascunhos] ou public [Explorar]). Paginado (page/limit, default 20, máx 50; use hasMore). Itens normalizados: imagem PRÓPRIA (history/favorites) traz imageId + url (use imageId em sapiens_image sourceImageIds ou sapiens_video startImageId/endImageId; ou a url em referenceImageUrls); acervo e characters são públicos/de terceiros, use a url (characters trazem mainImageUrl + imageUrls + characterId) em referenceImageUrls / startImageUrl / endImageUrl, NÃO em sourceImageIds. Personagens públicos também têm porta dedicada em sapiens_character action=list_public.",
|
|
161
|
+
schema: referenceSchema,
|
|
162
|
+
handler: reference,
|
|
163
|
+
},
|
|
134
164
|
};
|
|
135
165
|
// Guia de uso server-level: o protocolo MCP devolve isto no handshake (initialize)
|
|
136
166
|
// e TODO cliente (Helen no Hermes, Gemini CLI, Cursor, Antigravity) injeta no modelo.
|
|
@@ -140,6 +170,7 @@ const TOOLS = {
|
|
|
140
170
|
const SAPIENS_INSTRUCTIONS = `Você opera o Sapiens Sintéticos (sapiensinteticos.com) NA CONTA de um usuário logado. Cada tool age de verdade na conta dele e muitas COBRAM Sinapses (o crédito da casa). Aja como operador, não no chute.
|
|
141
171
|
|
|
142
172
|
REGRA DE OURO:
|
|
173
|
+
- PRIMEIRO CONTATO ou "o que você faz?"/"como começo?"/"o que dá pra fazer?": chame sapiens_meta action=start e MOSTRE o resultado na sua voz. Sem login, ele ensina a conectar; logado, traz saldo + primeiros poderes com exemplos. É a porta de entrada: não despeje a lista dos 26 tools, deixe o start guiar.
|
|
143
174
|
- Se um tool voltar erro de validação ("exige X", "falta Y"), LEIA o erro e refaça a chamada COM o que falta. NUNCA repita igual a chamada que falhou: 3 falhas seguidas no mesmo tool fazem o cliente marcar o servidor como "unreachable" por ~56s (disjuntor anti-loop). Aí parece que "o MCP caiu", quando foi só argumento faltando.
|
|
144
175
|
- Antes de gerar algo caro (imagem/música/vídeo), cheque saldo: sapiens_meta action=credits (ou subscription). Saldo baixo, avise o usuário antes.
|
|
145
176
|
- "sessionToken expirado" = refaça login: sapiens_meta action=login com o código de sapiensinteticos.com/conectar-claude.
|
|
@@ -158,8 +189,10 @@ Vídeo (sapiens_video) action=create EXIGE 'model': sapiens-video-seedance | kli
|
|
|
158
189
|
|
|
159
190
|
Imagem (sapiens_image) action=generate: prompt + model + aspectRatio. Referência via referenceImageUrls (galeria/Acervo/personagens) e/ou sourceImageIds. templateSlug aplica um super-prompt travado da casa.
|
|
160
191
|
|
|
192
|
+
Mídia no Fórum (PADRÃO, faça sempre assim): a peça vai EMBEDADA num card estruturado, nunca como "vai ouvir/ver noutro lugar". Música publicada (sapiens_musicator action=publish) já cria a tese no Fórum com a faixa tocável, não escreva "tá no Acervo, ouça lá". Pra anexar mídia a uma tese sua: sapiens_forum action=post com mediaTrackId (faixa pronta sua → card de música) OU mediaUrl + mediaKind (video|image; arquivo da casa, ou link YouTube/Vimeo pra vídeo). NÃO cole a URL da mídia no corpo da tese, use o campo: o corpo é só texto.
|
|
193
|
+
|
|
161
194
|
Voz da casa: 1ª pessoa, direto, anti-corporate, sem travessão. Pra bom entendedor, meia palavra basta.`;
|
|
162
|
-
const server = new Server({ name: "mcp-sapiens", version: "1.
|
|
195
|
+
const server = new Server({ name: "mcp-sapiens", version: "1.23.0" }, { capabilities: { tools: {} }, instructions: SAPIENS_INSTRUCTIONS });
|
|
163
196
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
164
197
|
tools: Object.entries(TOOLS).map(([name, t]) => ({
|
|
165
198
|
name,
|
|
@@ -192,4 +225,4 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
192
225
|
});
|
|
193
226
|
const transport = new StdioServerTransport();
|
|
194
227
|
await server.connect(transport);
|
|
195
|
-
console.error("mcp-sapiens v1.
|
|
228
|
+
console.error("mcp-sapiens v1.23.0 rodando via stdio (26 tools)");
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { convexQuery, getSessionToken } from "../convexClient.js";
|
|
3
|
+
/**
|
|
4
|
+
* sapiens_atlas — leitura do Atlas Ecossistema IA pelo Claude, TRAVADA na Lente
|
|
5
|
+
* do Ecossistema (equipamento pago). READ-ONLY: serve pra se informar, tirar
|
|
6
|
+
* dúvida e pedir leitura em cima dos dados (mapa de empresas da cadeia de valor
|
|
7
|
+
* da IA, sinais do X dos autores curados, conselho de vozes, briefings
|
|
8
|
+
* semanais). Identidade SEMPRE do sessionToken; quem não tem a Lente (nem é
|
|
9
|
+
* admin) recebe um aviso pra adquirir, sem dado nenhum.
|
|
10
|
+
*
|
|
11
|
+
* Sub-actions:
|
|
12
|
+
* - overview: ~52 empresas curadas (market cap, indústria, branding), o mapa.
|
|
13
|
+
* - signals: sinais recentes do X (posts dos autores). Filtros opcionais
|
|
14
|
+
* ticker (ex 'NVDA') ou authorHandle.
|
|
15
|
+
* - voices: o Conselho de Vozes — autores ativos + o digest mais recente
|
|
16
|
+
* de cada um (a destilação do que vêm dizendo).
|
|
17
|
+
* - briefings: o briefing semanal mais recente (conteúdo completo) + um
|
|
18
|
+
* histórico resumido dos anteriores.
|
|
19
|
+
*
|
|
20
|
+
* Nenhuma escrita: o Atlas pelo MCP é só pra consultar e raciocinar em cima.
|
|
21
|
+
*/
|
|
22
|
+
export const atlasSchema = z.object({
|
|
23
|
+
action: z.enum(["overview", "signals", "voices", "briefings"]),
|
|
24
|
+
limit: z
|
|
25
|
+
.number()
|
|
26
|
+
.optional()
|
|
27
|
+
.describe("Pra signals (default 30, máx 100) ou briefings (default 8, máx 52)."),
|
|
28
|
+
ticker: z
|
|
29
|
+
.string()
|
|
30
|
+
.optional()
|
|
31
|
+
.describe("Pra signals: filtra por ticker mencionado (ex 'NVDA', 'TSM')."),
|
|
32
|
+
authorHandle: z
|
|
33
|
+
.string()
|
|
34
|
+
.optional()
|
|
35
|
+
.describe("Pra signals: filtra pelos posts de um autor (@handle sem o @)."),
|
|
36
|
+
});
|
|
37
|
+
export async function atlas(args) {
|
|
38
|
+
const sessionToken = getSessionToken();
|
|
39
|
+
if (args.action === "overview") {
|
|
40
|
+
const companies = await convexQuery("atlasMcp:mcpOverview", { sessionToken });
|
|
41
|
+
return {
|
|
42
|
+
count: Array.isArray(companies) ? companies.length : 0,
|
|
43
|
+
companies,
|
|
44
|
+
note: "Mapa do ecossistema (empresas curadas). Pra sinais do X: action=signals. Pra leitura semanal: action=briefings.",
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
if (args.action === "signals") {
|
|
48
|
+
const signals = await convexQuery("atlasMcp:mcpSignals", {
|
|
49
|
+
sessionToken,
|
|
50
|
+
...(typeof args.limit === "number" ? { limit: args.limit } : {}),
|
|
51
|
+
...(args.ticker?.trim() ? { ticker: args.ticker.trim() } : {}),
|
|
52
|
+
...(args.authorHandle?.trim() ? { authorHandle: args.authorHandle.trim() } : {}),
|
|
53
|
+
});
|
|
54
|
+
return {
|
|
55
|
+
count: Array.isArray(signals) ? signals.length : 0,
|
|
56
|
+
signals,
|
|
57
|
+
note: "Sinais recentes do X. Pra ver as vozes curadas e seus digests: action=voices.",
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
if (args.action === "voices") {
|
|
61
|
+
const res = await convexQuery("atlasMcp:mcpVoices", { sessionToken });
|
|
62
|
+
return {
|
|
63
|
+
...res,
|
|
64
|
+
note: "Conselho de Vozes: authors (curados ativos) + digests (destilação mais recente por voz).",
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
if (args.action === "briefings") {
|
|
68
|
+
const res = await convexQuery("atlasMcp:mcpBriefings", {
|
|
69
|
+
sessionToken,
|
|
70
|
+
...(typeof args.limit === "number" ? { limit: args.limit } : {}),
|
|
71
|
+
});
|
|
72
|
+
return {
|
|
73
|
+
...res,
|
|
74
|
+
note: "latest = briefing semanal mais recente (completo); recent = resumo dos anteriores.",
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { convexMutation, convexQuery, getSessionToken } from "../convexClient.js";
|
|
3
|
+
/**
|
|
4
|
+
* sapiens_aula — CRUD do deck de aula (Mentoria OPS) via session token.
|
|
5
|
+
* ADMIN-ONLY (requireMcpAdmin no Convex): aula é conteúdo de mentor, não de
|
|
6
|
+
* aluno. Substitui o script migrate-aulas-to-convex.mjs: o Claude gera o deck
|
|
7
|
+
* e publica direto no Convex, fonte de verdade que o /aulas/<slug> lê.
|
|
8
|
+
*
|
|
9
|
+
* Sub-actions:
|
|
10
|
+
* - list: lista as aulas (slug/título/data/slideCount/voiceWarnings/url)
|
|
11
|
+
* - get: lê 1 aula completa por slug (pra editar local)
|
|
12
|
+
* - upsert: cria ou atualiza por slug (insere se não existe, senão patch)
|
|
13
|
+
*/
|
|
14
|
+
export const aulaSchema = z.object({
|
|
15
|
+
action: z.enum(["list", "get", "upsert"]),
|
|
16
|
+
slug: z
|
|
17
|
+
.string()
|
|
18
|
+
.optional()
|
|
19
|
+
.describe("kebab-case (a-z, 0-9, hífen), ex: 2026-06-25-minha-aula. Obrigatório pra get e upsert."),
|
|
20
|
+
aula: z
|
|
21
|
+
.any()
|
|
22
|
+
.optional()
|
|
23
|
+
.describe("Objeto completo da aula (obrigatório pra upsert). Campos top-level: title (obrig), subtitle?, data? (YYYY-MM-DD), duration?, tag?, mentorAgenda?, slides[] (≥1). Cada slide é { type, ...campos }. Tipos: cover (eyebrow/title/subtitle/meta), cover-image/section-image/content-image (imageUrl + campos), agenda (items:[{num,title,subtitle,time}]), content (eyebrow/title/body[markdown ou HTML]/list[]/listType), two-col (cols:[{h,body}]), quote (text/attribution), callout (tone:tip|warn|note/tag/body), comic (panels:[{imageUrl,caption}]), pause, close (eyebrow/title/items[]). Voz Sapiens: 1ª pessoa, anti-corporate, SEM travessão (—). O servidor re-linta a voz e grava voiceWarnings."),
|
|
24
|
+
});
|
|
25
|
+
function need(value, name) {
|
|
26
|
+
if (value === undefined || value === null) {
|
|
27
|
+
throw new Error(`Faltando arg "${name}" pra essa action.`);
|
|
28
|
+
}
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
export async function aula(args) {
|
|
32
|
+
const sessionToken = getSessionToken();
|
|
33
|
+
switch (args.action) {
|
|
34
|
+
case "list":
|
|
35
|
+
return await convexQuery("mcpExtras:mcpListAulas", { sessionToken });
|
|
36
|
+
case "get": {
|
|
37
|
+
const slug = need(args.slug, "slug");
|
|
38
|
+
const found = await convexQuery("mcpExtras:mcpGetAula", {
|
|
39
|
+
sessionToken,
|
|
40
|
+
slug,
|
|
41
|
+
});
|
|
42
|
+
return found ? { found: true, aula: found } : { found: false, slug };
|
|
43
|
+
}
|
|
44
|
+
case "upsert": {
|
|
45
|
+
const slug = need(args.slug, "slug");
|
|
46
|
+
const aulaObj = need(args.aula, "aula");
|
|
47
|
+
return await convexMutation("mcpExtras:mcpUpsertAula", {
|
|
48
|
+
sessionToken,
|
|
49
|
+
slug,
|
|
50
|
+
aula: aulaObj,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
package/dist/tools/forum.js
CHANGED
|
@@ -15,6 +15,10 @@ import { convexQuery, convexMutation, getSessionToken, } from "../convexClient.j
|
|
|
15
15
|
* - thread: o fio inteiro de um rootId (raiz + respostas), em ordem.
|
|
16
16
|
* - post: publica uma tese (humano). parentId pra responder; sem parentId
|
|
17
17
|
* abre fio novo. Se a conta for daemon, recusa e manda propor.
|
|
18
|
+
* Pode anexar um CARD DE MÍDIA estruturado: mediaTrackId (faixa
|
|
19
|
+
* pronta sua) vira player de música tocável na tese; mediaUrl +
|
|
20
|
+
* mediaKind (video|image) anexa vídeo/imagem (mídia da casa, ou
|
|
21
|
+
* link YouTube/Vimeo pra vídeo). Preferível a colar URL no corpo.
|
|
18
22
|
* - vote: ressoar (resonate) ou dissoar (dissonate) uma tese. Re-clicar o
|
|
19
23
|
* mesmo tipo tira o voto; clicar o oposto troca.
|
|
20
24
|
* - propose: o daemon propõe uma tese (precisa estar em Sintonia com um
|
|
@@ -55,6 +59,30 @@ export const forumSchema = z.object({
|
|
|
55
59
|
.number()
|
|
56
60
|
.optional()
|
|
57
61
|
.describe("Pra feed: quantas teses (default 20, máx 50)."),
|
|
62
|
+
mediaTrackId: z
|
|
63
|
+
.string()
|
|
64
|
+
.optional()
|
|
65
|
+
.describe("Pra post: anexa uma FAIXA pronta sua (o trackId do sapiens_musicator) como card de música tocável na tese. O servidor valida posse + status."),
|
|
66
|
+
mediaUrl: z
|
|
67
|
+
.string()
|
|
68
|
+
.optional()
|
|
69
|
+
.describe("Pra post: anexa vídeo/imagem por URL como card. Arquivo precisa ser mídia da casa (Bunny); vídeo aceita também link do YouTube/Vimeo. Use junto com mediaKind. (Música é via mediaTrackId, não aqui.)"),
|
|
70
|
+
mediaKind: z
|
|
71
|
+
.enum(["video", "image"])
|
|
72
|
+
.optional()
|
|
73
|
+
.describe("Pra post com mediaUrl: 'video' ou 'image'."),
|
|
74
|
+
mediaTitle: z
|
|
75
|
+
.string()
|
|
76
|
+
.optional()
|
|
77
|
+
.describe("Pra post com mídia: título da peça (faixa/vídeo)."),
|
|
78
|
+
mediaCoverUrl: z
|
|
79
|
+
.string()
|
|
80
|
+
.optional()
|
|
81
|
+
.describe("Pra post com mediaUrl video: poster/capa (mídia da casa/Bunny)."),
|
|
82
|
+
mediaAlt: z
|
|
83
|
+
.string()
|
|
84
|
+
.optional()
|
|
85
|
+
.describe("Pra post com mediaUrl image: texto alternativo da imagem."),
|
|
58
86
|
});
|
|
59
87
|
export async function forum(args) {
|
|
60
88
|
const sessionToken = getSessionToken();
|
|
@@ -85,11 +113,31 @@ export async function forum(args) {
|
|
|
85
113
|
if (!content) {
|
|
86
114
|
throw new Error("action=post exige content (o corpo da tese).");
|
|
87
115
|
}
|
|
116
|
+
// Card de mídia opcional: faixa por id (música, resolvida no servidor) OU
|
|
117
|
+
// vídeo/imagem por URL (validada lá pela allowlist de host). Os dois são
|
|
118
|
+
// mutuamente exclusivos; trackId tem precedência.
|
|
119
|
+
const mediaArgs = {};
|
|
120
|
+
if (args.mediaTrackId?.trim()) {
|
|
121
|
+
mediaArgs.mediaTrackId = args.mediaTrackId.trim();
|
|
122
|
+
}
|
|
123
|
+
else if (args.mediaUrl?.trim()) {
|
|
124
|
+
if (!args.mediaKind) {
|
|
125
|
+
throw new Error("Com mediaUrl, informe mediaKind ('video' ou 'image'). Música é via mediaTrackId.");
|
|
126
|
+
}
|
|
127
|
+
mediaArgs.media = {
|
|
128
|
+
kind: args.mediaKind,
|
|
129
|
+
url: args.mediaUrl.trim(),
|
|
130
|
+
...(args.mediaTitle?.trim() ? { title: args.mediaTitle.trim() } : {}),
|
|
131
|
+
...(args.mediaCoverUrl?.trim() ? { coverImageUrl: args.mediaCoverUrl.trim() } : {}),
|
|
132
|
+
...(args.mediaAlt?.trim() ? { alt: args.mediaAlt.trim() } : {}),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
88
135
|
const res = await convexMutation("forum:mcpCreatePost", {
|
|
89
136
|
sessionToken,
|
|
90
137
|
content,
|
|
91
138
|
...(args.title?.trim() ? { title: args.title.trim() } : {}),
|
|
92
139
|
...(args.parentId?.trim() ? { parentId: args.parentId.trim() } : {}),
|
|
140
|
+
...mediaArgs,
|
|
93
141
|
});
|
|
94
142
|
return {
|
|
95
143
|
...res,
|
package/dist/tools/meta.js
CHANGED
|
@@ -22,6 +22,7 @@ function readMcpVersion() {
|
|
|
22
22
|
}
|
|
23
23
|
export const metaSchema = z.object({
|
|
24
24
|
action: z.enum([
|
|
25
|
+
"start",
|
|
25
26
|
"login",
|
|
26
27
|
"logout",
|
|
27
28
|
"whoami",
|
|
@@ -126,7 +127,106 @@ const FORMAT_GUIDE = {
|
|
|
126
127
|
},
|
|
127
128
|
},
|
|
128
129
|
};
|
|
130
|
+
// Curadoria do action=start: a PORTA DE ENTRADA, não os 26 tools (despejar tudo
|
|
131
|
+
// afoga cliente fraco). 6 primeiros poderes, cada um com 1 frase pronta pra
|
|
132
|
+
// colar. Voz da casa, espelhando a vitrine /conectar-claude (mantido à mão aqui
|
|
133
|
+
// porque o MCP é pacote separado e não importa o examples.ts do app Next).
|
|
134
|
+
// NÃO menciona slash command (/sapiens:login etc.): isso só existe no Claude
|
|
135
|
+
// Code; Helen/Gemini/Cursor falam só MCP em linguagem natural.
|
|
136
|
+
const FIRST_POWERS = [
|
|
137
|
+
{
|
|
138
|
+
icon: "📚",
|
|
139
|
+
title: "Repertório (seu segundo cérebro)",
|
|
140
|
+
cost: "grátis",
|
|
141
|
+
try: "Acabei de ver Duna 2, adiciona no meu repertório, nota 9.",
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
icon: "🖼",
|
|
145
|
+
title: "Gerar imagem",
|
|
146
|
+
cost: "~400-500 Sinapses",
|
|
147
|
+
try: "Faz um retrato Sapiens de uma trader degen segurando uma moeda.",
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
icon: "✍️",
|
|
151
|
+
title: "Escrever artigo",
|
|
152
|
+
cost: "400 Sinapses",
|
|
153
|
+
try: "Escreve um ensaio sobre solidão usando meus jogos 9+ como lente.",
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
icon: "🧬",
|
|
157
|
+
title: "Persona (quiz dos 16 arquétipos)",
|
|
158
|
+
cost: "grátis",
|
|
159
|
+
try: "Quero fazer o quiz de persona aqui no chat.",
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
icon: "🪞",
|
|
163
|
+
title: "Reflexo (monta o seu Sintético)",
|
|
164
|
+
cost: "propor de graça · imagem 450",
|
|
165
|
+
try: "Monta o meu Reflexo a partir do meu rastro na plataforma.",
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
icon: "🎵",
|
|
169
|
+
title: "Música (Musicator)",
|
|
170
|
+
cost: "letra 300 · áudio 3000 Sinapses",
|
|
171
|
+
try: "Cria uma música sobre borderless na voz Sapiens e já renderiza.",
|
|
172
|
+
},
|
|
173
|
+
];
|
|
129
174
|
export async function meta(args) {
|
|
175
|
+
// start: porta de entrada do primeiro contato. NÃO exige login (esse é o
|
|
176
|
+
// ponto). Sem sessão, ensina a conectar; com sessão, personaliza com
|
|
177
|
+
// nome/tier/saldo e curadoria de primeiros poderes + um "comece por aqui".
|
|
178
|
+
if (args.action === "start") {
|
|
179
|
+
const token = getSessionToken();
|
|
180
|
+
if (!token) {
|
|
181
|
+
return {
|
|
182
|
+
connected: false,
|
|
183
|
+
message: "Bem-vindo ao Sapiens Sintéticos. Sua conta ainda não tá conectada aqui no cliente.",
|
|
184
|
+
howToConnect: [
|
|
185
|
+
"1. Entra logado em sapiensinteticos.com/conectar-claude e gera um código XXXX-XXXX (expira em 5 min, uso único).",
|
|
186
|
+
"2. Me passa esse código que eu conecto a sua conta (rodo sapiens_meta action=login).",
|
|
187
|
+
],
|
|
188
|
+
note: "Sem API key e sem cartão: você opera com as Sinapses que já tem na conta. Conectado, me peça 'o que você faz?' que eu te mostro os primeiros poderes.",
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
// best-effort: sem o whoami ainda dá pra entregar um onboarding útil.
|
|
192
|
+
let who = null;
|
|
193
|
+
try {
|
|
194
|
+
who = await convexQuery("mcpExtras:mcpGetMySubscription", {
|
|
195
|
+
sessionToken: token,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
// segue sem personalizar
|
|
200
|
+
}
|
|
201
|
+
const isAdmin = !!who?.user?.isAdmin;
|
|
202
|
+
const name = who?.user?.name || who?.user?.email || null;
|
|
203
|
+
const balance = who?.balance?.total ?? null;
|
|
204
|
+
const lowBalance = typeof balance === "number" && balance < 500;
|
|
205
|
+
const startHere = lowBalance
|
|
206
|
+
? "Com saldo curto pra geração paga, começa pelo que é de graça: marca umas obras no Repertório e faz o quiz de Persona. Eles alimentam todo o resto."
|
|
207
|
+
: "Começa pelo Repertório: marca uma obra que te pegou (filme, jogo, livro, anime). É grátis e vira a matéria-prima que eu puxo depois pra escrever, comparar e criar.";
|
|
208
|
+
return {
|
|
209
|
+
connected: true,
|
|
210
|
+
greeting: name
|
|
211
|
+
? `E aí, ${name}. Conectado${isAdmin ? " (tier admin)" : ""}${typeof balance === "number" ? `, ${balance} Sinapses na conta` : ""}.`
|
|
212
|
+
: "Conta conectada.",
|
|
213
|
+
tier: isAdmin ? "admin" : "user",
|
|
214
|
+
balance,
|
|
215
|
+
...(lowBalance
|
|
216
|
+
? {
|
|
217
|
+
balanceWarning: "Saldo abaixo de 500 Sinapses: o que é grátis roda tranquilo, mas pra imagem/música/vídeo talvez precise recarregar.",
|
|
218
|
+
}
|
|
219
|
+
: {}),
|
|
220
|
+
firstPowers: FIRST_POWERS,
|
|
221
|
+
startHere,
|
|
222
|
+
moreOnDemand: "Isso é só a porta de entrada. Tem também personagens (character-lock), voz (Helen TTS), vídeo, brand/design system, comunidade, fórum e suporte. Peça 'o que mais você faz?' que eu listo, ou já manda o que você quer fazer.",
|
|
223
|
+
...(isAdmin
|
|
224
|
+
? {
|
|
225
|
+
adminNote: "Tier admin (dono): pipeline editorial, Coluna Sapiens, blog e shorts/vídeo liberados, além de tudo do tier user.",
|
|
226
|
+
}
|
|
227
|
+
: {}),
|
|
228
|
+
};
|
|
229
|
+
}
|
|
130
230
|
if (args.action === "formats") {
|
|
131
231
|
return {
|
|
132
232
|
formats: FORMAT_GUIDE,
|
package/dist/tools/profile.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { convexQuery, getSessionToken } from "../convexClient.js";
|
|
2
|
+
import { convexQuery, convexMutation, getSessionToken } from "../convexClient.js";
|
|
3
3
|
/**
|
|
4
4
|
* sapiens_profile — o "tudo junto" do perfil do user (/u/<username>), leitura
|
|
5
5
|
* user-tier. Agrega o que mora no perfil mas estava espalhado/fora do MCP:
|
|
@@ -20,9 +20,25 @@ import { convexQuery, getSessionToken } from "../convexClient.js";
|
|
|
20
20
|
* - get: card completo (identidade + nível + saldo + badges + golden tools).
|
|
21
21
|
* - badges: só as conquistas (lista cheia).
|
|
22
22
|
* - golden_tools: só os favoritos do aitag (lista cheia).
|
|
23
|
+
* - notifications: suas notificações recentes (sino) + contagem de não-lidas.
|
|
24
|
+
* - mark_read: marca uma (notificationId) ou TODAS as suas não-lidas como lidas.
|
|
23
25
|
*/
|
|
24
26
|
export const profileSchema = z.object({
|
|
25
|
-
action: z.enum([
|
|
27
|
+
action: z.enum([
|
|
28
|
+
"get",
|
|
29
|
+
"badges",
|
|
30
|
+
"golden_tools",
|
|
31
|
+
"notifications",
|
|
32
|
+
"mark_read",
|
|
33
|
+
]),
|
|
34
|
+
limit: z
|
|
35
|
+
.number()
|
|
36
|
+
.optional()
|
|
37
|
+
.describe("Pra notifications: quantas trazer (default 15, máx 50)."),
|
|
38
|
+
notificationId: z
|
|
39
|
+
.string()
|
|
40
|
+
.optional()
|
|
41
|
+
.describe("Pra mark_read: o id de UMA notificação (vem de action=notifications). Omita pra marcar TODAS as não-lidas."),
|
|
26
42
|
});
|
|
27
43
|
const APP = "https://sapiensinteticos.com";
|
|
28
44
|
async function resolveUser(sessionToken) {
|
|
@@ -50,6 +66,32 @@ function mapTools(rows) {
|
|
|
50
66
|
}
|
|
51
67
|
export async function profile(args) {
|
|
52
68
|
const sessionToken = getSessionToken();
|
|
69
|
+
// notifications/mark_read autenticam pelo próprio sessionToken (requireMcpUser
|
|
70
|
+
// no Convex), não precisam do resolveUser — atalham antes.
|
|
71
|
+
if (args.action === "notifications") {
|
|
72
|
+
const res = await convexQuery("notifications:mcpListMine", {
|
|
73
|
+
sessionToken,
|
|
74
|
+
...(typeof args.limit === "number" ? { limit: args.limit } : {}),
|
|
75
|
+
});
|
|
76
|
+
return {
|
|
77
|
+
...res,
|
|
78
|
+
note: "Pra marcar lida: action=mark_read (com notificationId pra uma só, ou sem pra todas).",
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
if (args.action === "mark_read") {
|
|
82
|
+
const res = await convexMutation("notifications:mcpMarkRead", {
|
|
83
|
+
sessionToken,
|
|
84
|
+
...(args.notificationId?.trim()
|
|
85
|
+
? { notificationId: args.notificationId.trim() }
|
|
86
|
+
: {}),
|
|
87
|
+
});
|
|
88
|
+
return {
|
|
89
|
+
...res,
|
|
90
|
+
note: args.notificationId
|
|
91
|
+
? "Notificação marcada como lida."
|
|
92
|
+
: `Marquei ${res.updated} notificação(ões) como lida(s).`,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
53
95
|
const user = await resolveUser(sessionToken);
|
|
54
96
|
const userId = user._id;
|
|
55
97
|
if (args.action === "badges") {
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { convexAction, getSessionToken } from "../convexClient.js";
|
|
3
|
+
/**
|
|
4
|
+
* sapiens_reference — o "popup global de referência" do Sapiens via MCP.
|
|
5
|
+
*
|
|
6
|
+
* Espelha o modal "Selecionar Referência" do gerador web: um lugar só pra
|
|
7
|
+
* navegar os bancos e pegar o que vira referência em imagem (sapiens_image) ou
|
|
8
|
+
* vídeo (sapiens_video).
|
|
9
|
+
*
|
|
10
|
+
* Buckets (os 4 tabs do popup, completos):
|
|
11
|
+
* - history imagens recentes do user (privadas + públicas)
|
|
12
|
+
* - favorites imagens que o user curtiu (só as suas)
|
|
13
|
+
* - videos vídeos do user (Meus Vídeos)
|
|
14
|
+
* - acervo stock + comunidade (público); aceita `term` (busca) e `source`
|
|
15
|
+
* - characters personagens; `mode` = 'mine' (default) ou 'public'
|
|
16
|
+
*
|
|
17
|
+
* Cada item volta normalizado:
|
|
18
|
+
* - imagem PRÓPRIA (history/favorites): `imageId` + `url` → use imageId em
|
|
19
|
+
* sourceImageIds / startImageId / endImageId, ou url em referenceImageUrls.
|
|
20
|
+
* - acervo (stock/comunidade) e characters: use a `url` em referenceImageUrls
|
|
21
|
+
* (e startImageUrl/endImageUrl no vídeo). characters também trazem
|
|
22
|
+
* `characterId` + `imageUrls` (todas as imagens do personagem).
|
|
23
|
+
*/
|
|
24
|
+
export const referenceSchema = z.object({
|
|
25
|
+
action: z
|
|
26
|
+
.enum(["browse"])
|
|
27
|
+
.describe("Só 'browse' por enquanto: navega um bucket do acervo."),
|
|
28
|
+
bucket: z
|
|
29
|
+
.enum(["history", "favorites", "videos", "acervo", "characters"])
|
|
30
|
+
.describe("Qual banco navegar: 'history' (suas imagens recentes), 'favorites' (as que você curtiu), " +
|
|
31
|
+
"'videos' (seus vídeos), 'acervo' (stock + comunidade públicos), 'characters' (personagens)."),
|
|
32
|
+
page: z
|
|
33
|
+
.number()
|
|
34
|
+
.int()
|
|
35
|
+
.positive()
|
|
36
|
+
.optional()
|
|
37
|
+
.describe("Página (default 1). Use com hasMore pra paginar."),
|
|
38
|
+
limit: z
|
|
39
|
+
.number()
|
|
40
|
+
.int()
|
|
41
|
+
.positive()
|
|
42
|
+
.max(50)
|
|
43
|
+
.optional()
|
|
44
|
+
.describe("Itens por página (default 20, máx 50)."),
|
|
45
|
+
term: z
|
|
46
|
+
.string()
|
|
47
|
+
.optional()
|
|
48
|
+
.describe("Só bucket 'acervo': busca por texto (prompt/título/tag). Ex: 'house'."),
|
|
49
|
+
source: z
|
|
50
|
+
.enum(["all", "stock", "community"])
|
|
51
|
+
.optional()
|
|
52
|
+
.describe("Só bucket 'acervo': fonte. Default 'all' (stock + comunidade)."),
|
|
53
|
+
mode: z
|
|
54
|
+
.enum(["mine", "public"])
|
|
55
|
+
.optional()
|
|
56
|
+
.describe("Só bucket 'characters': 'mine' (seus personagens, inclui rascunhos; default) ou 'public' (catálogo do Explorar)."),
|
|
57
|
+
});
|
|
58
|
+
export async function reference(args) {
|
|
59
|
+
const sessionToken = getSessionToken();
|
|
60
|
+
if (args.action === "browse") {
|
|
61
|
+
const res = await convexAction("mcpReferences:referenceBrowse", {
|
|
62
|
+
sessionToken,
|
|
63
|
+
bucket: args.bucket,
|
|
64
|
+
page: args.page,
|
|
65
|
+
limit: args.limit,
|
|
66
|
+
term: args.term,
|
|
67
|
+
source: args.source,
|
|
68
|
+
mode: args.mode,
|
|
69
|
+
});
|
|
70
|
+
let note;
|
|
71
|
+
if (args.bucket === "videos") {
|
|
72
|
+
note =
|
|
73
|
+
"Cada item é um vídeo seu (imageId reusável em sapiens_video action=generate). Pra frame inicial/final de um vídeo NOVO use uma IMAGEM (bucket history/favorites/acervo/characters).";
|
|
74
|
+
}
|
|
75
|
+
else if (args.bucket === "acervo" || args.bucket === "characters") {
|
|
76
|
+
note =
|
|
77
|
+
"São referências públicas: use a `url` (characters trazem mainImageUrl + imageUrls) em sapiens_image referenceImageUrls, ou em sapiens_video startImageUrl/endImageUrl. Não são suas, então NÃO entram em sourceImageIds.";
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
note =
|
|
81
|
+
"Pra usar como referência: em sapiens_image passe o imageId em sourceImageIds (ou a url em referenceImageUrls); em sapiens_video passe startImageId/endImageId (ou startImageUrl/endImageUrl).";
|
|
82
|
+
}
|
|
83
|
+
return { ...res, note };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { convexQuery, convexMutation, getSessionToken, } from "../convexClient.js";
|
|
3
|
+
/**
|
|
4
|
+
* sapiens_support — abrir e tocar chamados de suporte do PRÓPRIO user pelo
|
|
5
|
+
* Claude (qualquer logado; identidade SEMPRE do sessionToken). Escopo travado
|
|
6
|
+
* no dono: você só vê e mexe nos SEUS tickets, nunca nos de outra pessoa.
|
|
7
|
+
*
|
|
8
|
+
* Sub-actions:
|
|
9
|
+
* - create: abre um chamado novo (subject obrigatório; message = primeira
|
|
10
|
+
* mensagem opcional; whatsapp opcional pra contato). Devolve ticketId.
|
|
11
|
+
* - list: seus chamados (subject, status open/closed, escalado, data).
|
|
12
|
+
* - get: um chamado seu (ticketId) com o histórico de mensagens.
|
|
13
|
+
* - reply: responde num chamado seu (entra como mensagem do user; se estava
|
|
14
|
+
* fechado, reabre).
|
|
15
|
+
*
|
|
16
|
+
* Fechar/escalar/deletar ficam no lado do suporte (fora do MCP).
|
|
17
|
+
*/
|
|
18
|
+
export const supportSchema = z.object({
|
|
19
|
+
action: z.enum(["create", "list", "get", "reply"]),
|
|
20
|
+
subject: z
|
|
21
|
+
.string()
|
|
22
|
+
.optional()
|
|
23
|
+
.describe("Pra create: o assunto do chamado."),
|
|
24
|
+
message: z
|
|
25
|
+
.string()
|
|
26
|
+
.optional()
|
|
27
|
+
.describe("Pra create: a primeira mensagem do chamado (opcional)."),
|
|
28
|
+
whatsapp: z
|
|
29
|
+
.string()
|
|
30
|
+
.optional()
|
|
31
|
+
.describe("Pra create: whatsapp de contato (opcional)."),
|
|
32
|
+
ticketId: z
|
|
33
|
+
.string()
|
|
34
|
+
.optional()
|
|
35
|
+
.describe("Pra get/reply: o id do chamado (vem de action=list)."),
|
|
36
|
+
content: z
|
|
37
|
+
.string()
|
|
38
|
+
.optional()
|
|
39
|
+
.describe("Pra reply: o texto da sua resposta."),
|
|
40
|
+
});
|
|
41
|
+
export async function support(args) {
|
|
42
|
+
const sessionToken = getSessionToken();
|
|
43
|
+
if (args.action === "create") {
|
|
44
|
+
if (!args.subject?.trim()) {
|
|
45
|
+
throw new Error("action=create exige subject (o assunto do chamado).");
|
|
46
|
+
}
|
|
47
|
+
const res = await convexMutation("support:mcpCreateTicket", {
|
|
48
|
+
sessionToken,
|
|
49
|
+
subject: args.subject.trim(),
|
|
50
|
+
...(args.message?.trim() ? { message: args.message.trim() } : {}),
|
|
51
|
+
...(args.whatsapp?.trim() ? { whatsapp: args.whatsapp.trim() } : {}),
|
|
52
|
+
});
|
|
53
|
+
return {
|
|
54
|
+
...res,
|
|
55
|
+
note: "Chamado aberto. Pra acompanhar: action=get ticketId=<ticketId>. Pra responder: action=reply.",
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
if (args.action === "list") {
|
|
59
|
+
const tickets = await convexQuery("support:mcpListMine", { sessionToken });
|
|
60
|
+
return {
|
|
61
|
+
count: Array.isArray(tickets) ? tickets.length : 0,
|
|
62
|
+
tickets,
|
|
63
|
+
note: "Seus chamados. Pra abrir um: action=get ticketId=<_id>.",
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
if (args.action === "get") {
|
|
67
|
+
if (!args.ticketId?.trim()) {
|
|
68
|
+
throw new Error("action=get exige ticketId (de action=list).");
|
|
69
|
+
}
|
|
70
|
+
return await convexQuery("support:mcpGetMine", {
|
|
71
|
+
sessionToken,
|
|
72
|
+
ticketId: args.ticketId.trim(),
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
if (args.action === "reply") {
|
|
76
|
+
if (!args.ticketId?.trim()) {
|
|
77
|
+
throw new Error("action=reply exige ticketId (de action=list).");
|
|
78
|
+
}
|
|
79
|
+
if (!args.content?.trim()) {
|
|
80
|
+
throw new Error("action=reply exige content (o texto da resposta).");
|
|
81
|
+
}
|
|
82
|
+
const res = await convexMutation("support:mcpReply", {
|
|
83
|
+
sessionToken,
|
|
84
|
+
ticketId: args.ticketId.trim(),
|
|
85
|
+
content: args.content.trim(),
|
|
86
|
+
});
|
|
87
|
+
return {
|
|
88
|
+
...res,
|
|
89
|
+
note: res.reopened
|
|
90
|
+
? "Resposta enviada. O chamado estava fechado e foi reaberto."
|
|
91
|
+
: "Resposta enviada no chamado.",
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
package/dist/tools/video.js
CHANGED
|
@@ -80,7 +80,26 @@ export const videoSchema = z.object({
|
|
|
80
80
|
.describe("'start' = imagem inicial (i2v), 'end' = frame final, 'driving' = vídeo de movimento (Motion)"),
|
|
81
81
|
}))
|
|
82
82
|
.optional()
|
|
83
|
-
.describe("References em base64. i2v: role 'start' (imagem). Motion: 'start' (pessoa) + 'driving' (vídeo de movimento, <=5MB)."),
|
|
83
|
+
.describe("References em base64 (escape hatch / Motion). i2v: role 'start' (imagem). Motion: 'start' (pessoa) + 'driving' (vídeo de movimento, <=5MB). Pra frame inicial/final a partir do seu acervo, prefira start/endImage* abaixo (sem precisar de base64)."),
|
|
84
|
+
// Frame inicial/final por REFERÊNCIA (resolvido server-side, igual à imagem):
|
|
85
|
+
// id da própria galeria OU url allowlist (galeria/Acervo/personagens). Mais
|
|
86
|
+
// simples que mandar base64. Descubra via sapiens_reference / sapiens_gallery.
|
|
87
|
+
startImageId: z
|
|
88
|
+
.string()
|
|
89
|
+
.optional()
|
|
90
|
+
.describe("Frame inicial (i2v): generatedImages:_id da SUA galeria. Vira reference role 'start'."),
|
|
91
|
+
startImageUrl: z
|
|
92
|
+
.string()
|
|
93
|
+
.optional()
|
|
94
|
+
.describe("Frame inicial (i2v): url pública (Bunny/Convex/Wikimedia) de galeria/Acervo/personagem. Vira reference role 'start'."),
|
|
95
|
+
endImageId: z
|
|
96
|
+
.string()
|
|
97
|
+
.optional()
|
|
98
|
+
.describe("Frame FINAL: generatedImages:_id da SUA galeria. Vira reference role 'end' (suporte varia por modelo)."),
|
|
99
|
+
endImageUrl: z
|
|
100
|
+
.string()
|
|
101
|
+
.optional()
|
|
102
|
+
.describe("Frame FINAL: url pública de galeria/Acervo/personagem. Vira reference role 'end' (suporte varia por modelo)."),
|
|
84
103
|
});
|
|
85
104
|
export async function video(args) {
|
|
86
105
|
const sessionToken = getSessionToken();
|
|
@@ -97,6 +116,10 @@ export async function video(args) {
|
|
|
97
116
|
resolution: args.resolution,
|
|
98
117
|
audio: args.audio,
|
|
99
118
|
references: args.references,
|
|
119
|
+
startImageId: args.startImageId,
|
|
120
|
+
startImageUrl: args.startImageUrl,
|
|
121
|
+
endImageId: args.endImageId,
|
|
122
|
+
endImageUrl: args.endImageUrl,
|
|
100
123
|
});
|
|
101
124
|
}
|
|
102
125
|
if (args.action === "generate") {
|
|
@@ -109,6 +132,10 @@ export async function video(args) {
|
|
|
109
132
|
prompt: args.prompt,
|
|
110
133
|
aspectRatio: args.aspectRatio,
|
|
111
134
|
references: args.references,
|
|
135
|
+
startImageId: args.startImageId,
|
|
136
|
+
startImageUrl: args.startImageUrl,
|
|
137
|
+
endImageId: args.endImageId,
|
|
138
|
+
endImageUrl: args.endImageUrl,
|
|
112
139
|
});
|
|
113
140
|
}
|
|
114
141
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sapiens-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.23.0",
|
|
4
4
|
"description": "MCP server pra operar o Sapiens Sintéticos (sapiensinteticos.com) pelo Claude Code: gerar imagem, escrever artigo, voz, música e mais, na sua conta. Login pelo código de sapiensinteticos.com/conectar-claude.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|