sapiens-mcp 1.43.4 → 1.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # sapiens-mcp
2
2
 
3
+ ![Helen Ailith numa mesa escura, antebracos vestidos por um exoesqueleto de luz verde, movendo placas de luz com as maos abertas](https://sapiensinteticos.b-cdn.net/borderlessprotocol/2026/07/1785430717302_e8p3i4.webp)
4
+
3
5
  Servidor MCP pra operar o [Sapiens Sintéticos](https://sapiensinteticos.com) direto do Claude Code, **na sua própria conta**. Você pede no Claude ("gera uma imagem disso", "escreve um artigo sobre aquilo") e ele faz, gastando as **suas Sinapses**, salvando no **seu perfil**.
4
6
 
5
7
  ## Pré-requisito
@@ -16,6 +18,8 @@ Precisa de Node 18+. A URL do backend já vem embutida; não precisa configurar
16
18
 
17
19
  ## Conectar sua conta
18
20
 
21
+ ![Helen segurando uma chave de luz verde entre duas portas: um retangulo de luz sem moldura e um terminal fisico](https://sapiensinteticos.b-cdn.net/borderlessprotocol/2026/07/1785430826064_avwjib.webp)
22
+
19
23
  1. Abra **[sapiensinteticos.com/conectar-claude](https://sapiensinteticos.com/conectar-claude)** logado e gere o código (`XXXX-XXXX`, vale 5 min).
20
24
  2. No Claude Code, peça pra logar (ele chama a ferramenta de login), ou rode direto:
21
25
 
@@ -25,6 +29,8 @@ O token de 30 dias fica salvo em `~/.sapiens-mcp/session.json`. Pra sair: `sapie
25
29
 
26
30
  ## O que dá pra pedir (e o custo em Sinapses)
27
31
 
32
+ ![Helen com um braco estendido, orbitada por uma lente, rolos de filme, um vinil, um caderno aberto e um microfone de estudio](https://sapiensinteticos.b-cdn.net/borderlessprotocol/2026/07/1785430870745_waml5a.webp)
33
+
28
34
  | O que | Custo |
29
35
  |---|---|
30
36
  | Gerar imagem (vai pra sua galeria) | ~400-500 |
package/dist/registry.js CHANGED
@@ -42,7 +42,7 @@ import { skillMenuLine } from "./skills.js";
42
42
  */
43
43
  export const TOOLS = {
44
44
  sapiens_pipeline: {
45
- description: "CRUD do content pipeline Sapiens (sources/productions/publishables). Sub-actions: list_sources, list_articles (use includeDrafts pra incluir drafts; onlyAvailable pra esconder os já virados em source), get_source, get_production, list_versions, add_article_as_source, create_draft_article_and_source (seed), create_production (sourceId+format → productionId draft), update_production (substitui payload, opcionalmente muda status), finalize_production (cria publishable v1, v2... com snapshot), remove_production, remove_source, set_source_done, update_source_notes, restore_version (volta payload duma versão antiga), set_publishable_title (renomeia um publishable), backfill_via (rotula em lote o campo 'via' das productions antigas; dryRun=true só lista), propose_mega_grafico_plan (granular: só gera plano via Gemini, devolve fullPrompt+spec), run_mega_grafico_full (ONE-SHOT, recomendado: cria production+propõe plano+gera imagem+aplica selo Sapiens+finaliza publishable numa chamada só), generate_carousel (gera um carrossel editorial standalone a partir de brief OU articleId — 7-9 slides na voz Sapiens + imagens do banco; devolve id + url do editor pro humano abrir, ajustar e exportar; admin-only, cobra Sinapses, reembolsa se falhar). generate_carousel_production (o IRMÃO PIPELINE do generate_carousel: recebe um sourceId de artigo, CRIA a production carrossel_ig e a preenche pelo MESMO motor da casa, já no shape editorial (templateId+slots) que o editor de pipeline renderiza — prefira ESTE a create_production+payload cru pro carrossel, que abre VAZIO no editor; devolve productionId + url do editor de pipeline; admin-only, cobra Sinapses, reembolsa se falhar). CARROSSEL FINO (edição sem tela, admin-only): list_carousels (seus carrosséis standalone), get_carousel (payload completo + catálogo de templates com slots/limites + paletas — tudo pra VOCÊ escrever os slides), update_carousel (payload inteiro de volta, sanitizado no servidor; preserve os campos image dos slides que não mexeu), carousel_auto_images (IA escolhe imagens do banco pros slides de foto, 60 Sinapses; makeVisual=true converte slides de texto pra layouts de foto antes — ritmo visual; generateMissing=true gera imagem nova pros que o banco não cobrir, ~450 Sinapses cada, até 4, avise o custo antes), carousel_generate_image (imagem nova pra UM slide, ~450 Sinapses, customPrompt opcional). Fluxo confortável (standalone): generate_carousel → get_carousel → update_carousel (afia os textos) → carousel_auto_images → humano abre a url pra exportar. Fluxo pipeline (carrossel atrelado a um artigo/source): generate_carousel_production (sourceId) → humano abre a url do editor de pipeline pra revisar e finalizar (finalize_production). Pra mega_grafico, SEMPRE prefira run_mega_grafico_full em vez de sequenciar manualmente (menos drift). Idempotente só na production (passa productionId pra reusar a MESMA row), mas cada run RE-GERA a imagem e cobra de novo (~900 Sinapses): não é grátis re-rodar. OBRIGATÓRIO perguntar ao user antes se withHelen=true (cartoon Helen interage com tema, ~15-25% do poster) ou false (poster 100% diagramático). Custo ~900-1000 sinapses por geração. run_mega_grafico_full, generate_carousel, generate_carousel_production, carousel_auto_images (com generateMissing) e carousel_generate_image são SÍNCRONAS e pesadas: vale a REGRA DO TIMEOUT (podem cobrar mesmo voltando 'Timeout'; cheque get_carousel/dashboard antes de repetir). Use skipFinalize=true se quiser deixar production em 'ready' pro admin revisar antes de publishable. Payload livre por formato — chame sapiens_meta action=formats pra ver schemas sugeridos.",
45
+ description: "CRUD do content pipeline Sapiens (sources/productions/publishables). Sub-actions: list_sources, list_articles (use includeDrafts pra incluir drafts; onlyAvailable pra esconder os já virados em source), get_source, get_production, list_versions, add_article_as_source, create_draft_article_and_source (seed), create_production (sourceId+format → productionId draft), update_production (substitui payload, opcionalmente muda status), finalize_production (cria publishable v1, v2... com snapshot), remove_production, remove_source, set_source_done, update_source_notes, restore_version (volta payload duma versão antiga), set_publishable_title (renomeia um publishable), backfill_via (rotula em lote o campo 'via' das productions antigas; dryRun=true só lista), propose_mega_grafico_plan (granular: só gera plano via Gemini, devolve fullPrompt+spec), run_mega_grafico_full (ONE-SHOT, recomendado: cria production+propõe plano+gera imagem+aplica selo Sapiens+finaliza publishable numa chamada só), generate_carousel (gera um carrossel editorial standalone a partir de brief OU articleId — 7-9 slides na voz Sapiens + imagens do banco; devolve id + url do editor pro humano abrir, ajustar e exportar; admin-only, cobra Sinapses, reembolsa se falhar; model opcional trava uma folha registrada da casa, ex 'feed' ou 'reflexao', em vez do caminho editorial livre). generate_carousel_production (o IRMÃO PIPELINE do generate_carousel: recebe um sourceId de artigo, CRIA a production carrossel_ig e a preenche pelo MESMO motor da casa, já no shape editorial (templateId+slots) que o editor de pipeline renderiza — prefira ESTE a create_production+payload cru pro carrossel, que abre VAZIO no editor; devolve productionId + url do editor de pipeline; admin-only, cobra Sinapses, reembolsa se falhar; model opcional trava a folha registrada; beats opcional entrega copy PRONTO por beat da espinha, [{n, slots}], quando o agente JÁ escreveu e validou os blocos: com beats o Gemini de escrita NÃO roda e a rodada cobra só o picker de imagens, zero com autoPickImages=false; caption opcional vira a legenda do meta). CARROSSEL FINO (edição sem tela, admin-only): list_carousels (seus carrosséis standalone), get_carousel (payload completo + catálogo de templates com slots/limites + paletas — tudo pra VOCÊ escrever os slides), update_carousel (payload inteiro de volta, sanitizado no servidor; preserve os campos image dos slides que não mexeu), carousel_auto_images (IA escolhe imagens do banco pros slides de foto, 60 Sinapses; aceita carouselId standalone OU productionId de carrossel da pipeline; makeVisual=true converte slides de texto pra layouts de foto antes — ritmo visual; generateMissing=true gera imagem nova pros que o banco não cobrir, ~450 Sinapses cada, até 4, avise o custo antes), carousel_generate_image (imagem nova pra UM slide, ~450 Sinapses, customPrompt opcional). Fluxo confortável (standalone): generate_carousel → get_carousel → update_carousel (afia os textos) → carousel_auto_images → humano abre a url pra exportar. Fluxo pipeline (carrossel atrelado a um artigo/source): generate_carousel_production (sourceId) → humano abre a url do editor de pipeline pra revisar e finalizar (finalize_production). Fluxo pipeline com copy do agente (a skill escreveu os blocos): generate_carousel_production (sourceId + model + beats) → carousel_auto_images (productionId) se faltar foto → humano revisa e finaliza. Pra mega_grafico, SEMPRE prefira run_mega_grafico_full em vez de sequenciar manualmente (menos drift). Idempotente só na production (passa productionId pra reusar a MESMA row), mas cada run RE-GERA a imagem e cobra de novo (~900 Sinapses): não é grátis re-rodar. OBRIGATÓRIO perguntar ao user antes se withHelen=true (cartoon Helen interage com tema, ~15-25% do poster) ou false (poster 100% diagramático). Custo ~900-1000 sinapses por geração. run_mega_grafico_full, generate_carousel, generate_carousel_production, carousel_auto_images (com generateMissing) e carousel_generate_image são SÍNCRONAS e pesadas: vale a REGRA DO TIMEOUT (podem cobrar mesmo voltando 'Timeout'; cheque get_carousel/dashboard antes de repetir). Use skipFinalize=true se quiser deixar production em 'ready' pro admin revisar antes de publishable. Payload livre por formato — chame sapiens_meta action=formats pra ver schemas sugeridos.",
46
46
  schema: pipelineSchema,
47
47
  handler: pipeline,
48
48
  },
@@ -107,7 +107,7 @@ export const TOOLS = {
107
107
  handler: persona,
108
108
  },
109
109
  sapiens_helen: {
110
- description: "Helen Voice TTS via ElevenLabs ou Google Gemini (qualquer logado; cobra Sinapses). Sub-actions: list_presets (catálogo de voiceIds/voiceNames recomendados + stylePreambles), speak (sintetiza, retorna audioBase64+mimeType+sizeBytes). BYOK suportado via clientApiKey, senão usa env do deploy. Custo: ElevenLabs ~$0.30/500c, Google ~$0.01/500c. Max 5000 chars (quebra antes em chunks). text pré-processado pelo caller (sem em-dash, sem markdown).",
110
+ description: "Helen Voice TTS via ElevenLabs, Google Gemini ou Fish Audio (qualquer logado; cobra Sinapses). Sub-actions: list_presets (catálogo de voiceIds/voiceNames recomendados + stylePreambles), speak (sintetiza, retorna audioBase64+mimeType+sizeBytes). provider=fish é o S2.1 Pro, o mais forte em japonês, mandarim e coreano: fala de personagem e cena de anime pedem ele. `language` escolhe o idioma (auto|pt|en|ja|es|ko|zh|fr|it|de). BYOK suportado via clientApiKey, senão usa env do deploy. Custo: ElevenLabs ~$0.30/500c, Google ~$0.01/500c, Fish grátis via API até 31/08/2026. Max 5000 chars (quebra antes em chunks). text pré-processado pelo caller (sem em-dash, sem markdown).",
111
111
  schema: helenSchema,
112
112
  handler: helen,
113
113
  },
@@ -122,7 +122,7 @@ export const TOOLS = {
122
122
  handler: shorts,
123
123
  },
124
124
  sapiens_video: {
125
- 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; aceita até 4 imagens de REFERÊNCIA via referenceImageIds/referenceImageUrls/referenceImagePaths (os Veo fast/quality também aceitam, até 3; Lite/Kling/WAN/Omni não), que guiam estilo/personagem/composição SEM virar o 1º frame — é o fluxo STORYBOARD: gere a folha de key poses com sapiens_image templateSlug='storyboard-sapiens-v1', passe folha + personagem como refs num t2v e descreva o take contínuo no prompt, citando as refs por descrição e mandando ignorar o traço do sketch; aceita também 1 VÍDEO DE MOVIMENTO via referenceVideoUrls (role 'refvideo' -> reference_videos, <=15s, host da casa): a coreografia/câmera do clipe guia o take, combinável com a folha), 'sapiens-video-seedance-2-fast' e 'sapiens-video-seedance-2-mini' (os irmãos do 2.0: MESMO repertório completo, incluindo referência, frame final e vídeo de movimento; o Fast custa 20% menos e o Mini METADE, ambos com teto 720p — pedir 1080p neles entrega e cobra 720p. Use o Mini pra iterar enquadramento/prompt barato e feche no 'sapiens-video-seedance' quando o take estiver certo), '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-shot-mimic' (Shot Mimic: recria o plano/câmera/cortes de um vídeo de referência como cena nova), 'sapiens-video-omni' (Gemini Omni: texto vira vídeo 10s 720p com áudio nativo; NÃO aceita mídia do user, ignora references/durationSec/resolution; editOfImageId aponta um vídeo Omni seu e o prompt edita a MESMA cena, preservando câmera e ambiente), '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. FRAME POR ARQUIVO LOCAL (só no MCP instalado/stdio, não no remoto): startImagePath/endImagePath = caminho absoluto de uma imagem no seu PC (PNG/JPEG/WebP até 8MB); o processo lê o arquivo e sobe como frame inicial/final, igual a subir no gerador do site — 1 imagem inicial + 1 final por vídeo, então pra vários vídeos rode create uma vez por imagem. No remoto use id/url. 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}. VITRINE (sem custo): sub-action 'demos' lista os SEUS demo films (kind=demo do Estúdio de Vídeo) com slug + estado de vitrine; sub-action 'showcase' põe/tira um demo (por slug) do mini-cinema da /conectar-claude, com showcaseTag (chip de capacidade) e showcaseOrder (ordem asc). Fluxo: 'demos' pra achar o slug, depois 'showcase' com showcase=true. Só entra na vitrine pública se for a conta da casa. VÍDEOS PROGRAMÁTICOS (ADMIN, sem custo): o Lab do Estúdio de Vídeo (/experimentos/films, tabela videoSpecs, 5 kinds: demo | aula-tour | essay | tipografia-musical | dataviz) opera por aqui sem browser — 'film-list' (todos os kinds; filtros filmKind/filmStatus), 'film-get' (spec inteiro por slug), 'film-upsert' (cria/atualiza por slug, idempotente; spec = objeto JSON no shape do 'Copiar spec' da tela, validação no servidor), 'film-status' (produção por slug: filmStatus + videoUrl + durationSecMeasured; o fecho do render é os três num call), 'film-publish' (Acervo aba Fitas + portfólio; exige pronto+URL), 'film-delete' (limpar rascunho). O RENDER do filme segue no agente local (skill /film, repo da casa): o MCP registra e fecha o ciclo, não renderiza. create é ASSÍNCRONA: cria o row, debita e volta NA HORA com {imageId, status:'rendering', cost} (não espera o render, que leva de segundos a minutos). Acompanhe com a sub-action 'status' (imageId) até status='completed' (traz a url) ou 'error'/'blocked'. NÃO chame create de novo enquanto renderiza (cria outro vídeo e cobra de novo); falha de provider refunda sozinha. SOM: 'sonorize' (imageId de vídeo SEU completed + prompt do som da cena) gera uma VARIANTE nova com trilha sincronizada (20 Sinapses/s, o original fica intacto; sonorize sempre o original, nunca uma variante). ADMIN: 'shadows' (videoUrl + title) extrai o deepshadow (depth-map) de um vídeo: entra na sua timeline de vídeos e no Acervo como driving reutilizável; 'shadows-list' lista os deepshadows prontos. Sub-action 'models' (sem custo, sem login): lista os modelos de vídeo ativos + preço-piso + config (durações/resoluções) + disponibilidade (Omni depende de env).",
125
+ 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; aceita até 4 imagens de REFERÊNCIA via referenceImageIds/referenceImageUrls/referenceImagePaths (os Veo fast/quality também aceitam, até 3; Lite/Kling/WAN/Omni não), que guiam estilo/personagem/composição SEM virar o 1º frame — é o fluxo STORYBOARD: gere a folha de key poses com sapiens_image templateSlug='storyboard-sapiens-v1', passe folha + personagem como refs num t2v e descreva o take contínuo no prompt, citando as refs por descrição e mandando ignorar o traço do sketch; aceita também 1 VÍDEO DE MOVIMENTO via referenceVideoUrls (role 'refvideo' -> reference_videos, <=15s, host da casa): a coreografia/câmera do clipe guia o take, combinável com a folha), 'sapiens-video-seedance-2-fast' e 'sapiens-video-seedance-2-mini' (os irmãos do 2.0: MESMO repertório completo, incluindo referência, frame final e vídeo de movimento; o Fast custa 20% menos e o Mini METADE, ambos com teto 720p — pedir 1080p neles entrega e cobra 720p. Use o Mini pra iterar enquadramento/prompt barato e feche no 'sapiens-video-seedance' quando o take estiver certo), 'sapiens-video-kling' (Kling 3.0 Pro, anima imagem, 3-15s, sound opcional, i2v/t2v), 'sapiens-video-hailuo' (Hailuo 2.3 da MiniMax, física e movimento em 768p, 6 ou 10s, t2v/i2v) e 'sapiens-video-hailuo-pro' (o mesmo em 1080p, 5s fixo — duração não é param aqui): motores PUROS, sem áudio nativo, sem imagens de referência e sem frame final, então quem precisa disso fica no Seedance 2.0; o Pro é o 1080p mais barato da casa depois do Seedance 1.0 Fast, '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-shot-mimic' (Shot Mimic: recria o plano/câmera/cortes de um vídeo de referência como cena nova), 'sapiens-video-omni' (Gemini Omni: texto vira vídeo 10s 720p com áudio nativo; NÃO aceita mídia do user, ignora references/durationSec/resolution; editOfImageId aponta um vídeo Omni seu e o prompt edita a MESMA cena, preservando câmera e ambiente), '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. FRAME POR ARQUIVO LOCAL (só no MCP instalado/stdio, não no remoto): startImagePath/endImagePath = caminho absoluto de uma imagem no seu PC (PNG/JPEG/WebP até 8MB); o processo lê o arquivo e sobe como frame inicial/final, igual a subir no gerador do site — 1 imagem inicial + 1 final por vídeo, então pra vários vídeos rode create uma vez por imagem. No remoto use id/url. 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}. VITRINE (sem custo): sub-action 'demos' lista os SEUS demo films (kind=demo do Estúdio de Vídeo) com slug + estado de vitrine; sub-action 'showcase' põe/tira um demo (por slug) do mini-cinema da /conectar-claude, com showcaseTag (chip de capacidade) e showcaseOrder (ordem asc). Fluxo: 'demos' pra achar o slug, depois 'showcase' com showcase=true. Só entra na vitrine pública se for a conta da casa. VÍDEOS PROGRAMÁTICOS (ADMIN, sem custo): o Lab do Estúdio de Vídeo (/experimentos/films, tabela videoSpecs, 5 kinds: demo | aula-tour | essay | tipografia-musical | dataviz) opera por aqui sem browser — 'film-list' (todos os kinds; filtros filmKind/filmStatus), 'film-get' (spec inteiro por slug), 'film-upsert' (cria/atualiza por slug, idempotente; spec = objeto JSON no shape do 'Copiar spec' da tela, validação no servidor), 'film-status' (produção por slug: filmStatus + videoUrl + durationSecMeasured; o fecho do render é os três num call), 'film-publish' (Acervo aba Fitas + portfólio; exige pronto+URL), 'film-delete' (limpar rascunho). O RENDER do filme segue no agente local (skill /film, repo da casa): o MCP registra e fecha o ciclo, não renderiza. create é ASSÍNCRONA: cria o row, debita e volta NA HORA com {imageId, status:'rendering', cost} (não espera o render, que leva de segundos a minutos). Acompanhe com a sub-action 'status' (imageId) até status='completed' (traz a url) ou 'error'/'blocked'. NÃO chame create de novo enquanto renderiza (cria outro vídeo e cobra de novo); falha de provider refunda sozinha. SOM: 'sonorize' (imageId de vídeo SEU completed + prompt do som da cena) gera uma VARIANTE nova com trilha sincronizada (20 Sinapses/s, o original fica intacto; sonorize sempre o original, nunca uma variante). ADMIN: 'shadows' (videoUrl + title) extrai o deepshadow (depth-map) de um vídeo: entra na sua timeline de vídeos e no Acervo como driving reutilizável; 'shadows-list' lista os deepshadows prontos. Sub-action 'models' (sem custo, sem login): lista os modelos de vídeo ativos + preço-piso + config (durações/resoluções) + disponibilidade (Omni depende de env). LEGENDA (fita, por slug): 'caption-list' (sem custo, mostra os idiomas que a peça já tem), 'caption-generate' (captionLang = idioma FALADO; captionTrio=true já traduz pro trio da casa pt+en+ja na mesma chamada) e 'caption-translate' (captionLang = destino, parte sempre da faixa original). Cobra por minuto começado de vídeo: 50 Sinapses o minuto transcrito, 20 o traduzido, com a duração vindo do doc da peça. A legenda é desenhada pela casa em dois estilos (discreta e social), trocáveis no play sem regerar e sem custo.",
126
126
  schema: videoSchema,
127
127
  handler: video,
128
128
  },
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { convexAction, getSessionToken } from "../convexClient.js";
3
3
  /**
4
- * Helen Voice — Text-to-Speech via ElevenLabs ou Google Gemini.
4
+ * Helen Voice — Text-to-Speech via ElevenLabs, Google Gemini ou Fish Audio.
5
5
  *
6
6
  * Sub-actions:
7
7
  * - speak: sintetiza fala. Retorna audioBase64 + mimeType + sizeBytes.
@@ -18,13 +18,13 @@ export const helenSchema = z.object({
18
18
  .optional()
19
19
  .describe("Texto a falar (action=speak). Max 5000 chars."),
20
20
  provider: z
21
- .enum(["elevenlabs", "google"])
21
+ .enum(["elevenlabs", "google", "fish"])
22
22
  .optional()
23
- .describe("elevenlabs (melhor natural, mais caro) ou google (Gemini TTS, mais barato). Default elevenlabs."),
23
+ .describe("elevenlabs (inglês expressivo, mais caro), google (Gemini TTS, mais barato) ou fish (S2.1 Pro, o melhor em japonês e nas línguas asiáticas). Default elevenlabs."),
24
24
  modelId: z
25
25
  .string()
26
26
  .optional()
27
- .describe("ElevenLabs: 'eleven_v3' (default) ou 'eleven_multilingual_v2'. Google: 'gemini-3.1-flash-tts-preview' (default)."),
27
+ .describe("ElevenLabs: 'eleven_v3' (default) ou 'eleven_multilingual_v2'. Google: 'gemini-3.1-flash-tts-preview' (default). Fish: escolhido no servidor, o que vier aqui é ignorado."),
28
28
  voiceId: z
29
29
  .string()
30
30
  .optional()
@@ -33,10 +33,18 @@ export const helenSchema = z.object({
33
33
  .string()
34
34
  .optional()
35
35
  .describe("Google prebuilt voice (default Kore). Use list_presets pra ver opções."),
36
+ fishReferenceId: z
37
+ .string()
38
+ .optional()
39
+ .describe("Fish: id da voz no acervo do fish.audio. Sem ele vai a voz default do modelo."),
40
+ language: z
41
+ .enum(["auto", "pt", "en", "ja", "es", "ko", "zh", "fr", "it", "de"])
42
+ .optional()
43
+ .describe("Idioma da fala. Cada motor recebe no dialeto dele; fish e google detectam pelo texto, então lá isto é informativo. Default pt."),
36
44
  languageCode: z
37
45
  .string()
38
46
  .optional()
39
- .describe("ElevenLabs: ex 'pt' pra PT-BR otimizado."),
47
+ .describe("ElevenLabs: ex 'pt' pra PT-BR otimizado. Vence o `language` quando os dois vêm."),
40
48
  stylePreamble: z
41
49
  .string()
42
50
  .optional()
@@ -85,6 +93,16 @@ const PRESETS = {
85
93
  "Read this slow and contemplative, with pauses:",
86
94
  ],
87
95
  },
96
+ fish: {
97
+ note: "Fish Audio S2.1 Pro. Não precisa de modelId (o servidor escolhe) e detecta o idioma pelo texto. É o motor mais forte da casa em japonês, mandarim e coreano: cena de anime, fala de personagem, narração em ja pedem ele. Voz opcional em fishReferenceId; sem ela, a voz default.",
98
+ voices: [
99
+ {
100
+ fishReferenceId: null,
101
+ name: "Voz default do modelo",
102
+ use: "o caminho normal: escreve o texto no idioma que quer e manda",
103
+ },
104
+ ],
105
+ },
88
106
  };
89
107
  export async function helen(args) {
90
108
  if (args.action === "list_presets") {
@@ -98,9 +116,13 @@ export async function helen(args) {
98
116
  const modelId = args.modelId ??
99
117
  (provider === "elevenlabs"
100
118
  ? "eleven_v3"
101
- // 3.1 Flash é o único TTS do Google que gera nas contas free da casa
102
- // (o 2.5 responde 200 sem áudio nelas e sai na conta central).
103
- : "gemini-3.1-flash-tts-preview");
119
+ : provider === "fish"
120
+ // O motor real do Fish é o header `model` do servidor. Este id viaja só
121
+ // pra log e pra chave de cache não misturar os motores.
122
+ ? "s2.1-pro-free"
123
+ // 3.1 Flash é o único TTS do Google que gera nas contas free da casa
124
+ // (o 2.5 responde 200 sem áudio nelas e só sai na conta central).
125
+ : "gemini-3.1-flash-tts-preview");
104
126
  if (provider === "elevenlabs" && !args.voiceId) {
105
127
  throw new Error("provider=elevenlabs exige voiceId. Use action=list_presets pra ver opções.");
106
128
  }
@@ -112,6 +134,8 @@ export async function helen(args) {
112
134
  modelId,
113
135
  voiceId: args.voiceId,
114
136
  googleVoiceName: args.googleVoiceName,
137
+ fishReferenceId: args.fishReferenceId,
138
+ language: args.language,
115
139
  languageCode: args.languageCode,
116
140
  stylePreamble: args.stylePreamble,
117
141
  voiceSettings: args.voiceSettings,
@@ -89,11 +89,33 @@ export const pipelineSchema = z.object({
89
89
  .boolean()
90
90
  .optional()
91
91
  .describe("generate_carousel: se TRUE, a voz do Sintético do dono dirige o copy (amplificar). Default false = estilo do dono."),
92
+ model: z
93
+ .string()
94
+ .optional()
95
+ .describe("generate_carousel / generate_carousel_production: folha travada registrada da casa (ex: 'feed', 'reflexao'). A espinha de slides vem do servidor; a IA (ou seus beats) só escreve a prosa. Ausente = caminho editorial livre."),
96
+ beats: z
97
+ .union([
98
+ z.array(z.object({
99
+ n: z.number().describe("número do beat na espinha (1 a 9)"),
100
+ slots: z
101
+ .record(z.string())
102
+ .describe("só os slots de PROSA daquele beat (title/body/punch/tagline...), já contados e na voz"),
103
+ })),
104
+ // Defesa: cliente que serializa o array como string JSON (mesmo caso do
105
+ // arg payload). O handler normaliza.
106
+ z.string(),
107
+ ])
108
+ .optional()
109
+ .describe("generate_carousel_production: copy PRONTO por beat da espinha, [{n, slots}]. Exige model. Com beats o Gemini de escrita NÃO roda e a rodada cobra só o picker de imagens (zero com autoPickImages=false). Use quando a skill do carrossel já escreveu e validou os blocos (o contador emite este JSON com --beats)."),
110
+ keyword: z
111
+ .string()
112
+ .optional()
113
+ .describe("generate_carousel_production (com beats): a palavra única do comment-to-DM, vira meta.keyword do pacote de publicação."),
92
114
  // carrossel standalone (get/update/imagens)
93
115
  carouselId: z
94
116
  .string()
95
117
  .optional()
96
- .describe("carousels_standalone:_id — get_carousel/update_carousel/carousel_auto_images/carousel_generate_image. Vem de generate_carousel ou list_carousels."),
118
+ .describe("carousels_standalone:_id — get_carousel/update_carousel/carousel_auto_images/carousel_generate_image. Vem de generate_carousel ou list_carousels. No carousel_auto_images, alternativa: productionId (carrossel da pipeline)."),
97
119
  slideId: z
98
120
  .string()
99
121
  .optional()
@@ -129,7 +151,7 @@ export const pipelineSchema = z.object({
129
151
  productionId: z
130
152
  .string()
131
153
  .optional()
132
- .describe("contentProductions:_id — get_production/update_production/finalize_production/remove_production/list_versions. Vem de create_production."),
154
+ .describe("contentProductions:_id — get_production/update_production/finalize_production/remove_production/list_versions/carousel_auto_images (carrossel da pipeline). Vem de create_production ou generate_carousel_production."),
133
155
  publishableId: z
134
156
  .string()
135
157
  .optional()
@@ -191,6 +213,25 @@ function coercePayloadArg(raw) {
191
213
  throw new Error('Arg "payload" veio como string mas não é JSON válido. Mande o objeto do payload.');
192
214
  }
193
215
  }
216
+ // Mesma defesa pro `beats` do generate_carousel_production: aceita o array já
217
+ // estruturado ou a string JSON dele, e valida o shape mínimo antes de mandar.
218
+ function coerceBeatsArg(raw) {
219
+ if (raw == null)
220
+ return undefined;
221
+ let val = raw;
222
+ if (typeof val === "string") {
223
+ try {
224
+ val = JSON.parse(val);
225
+ }
226
+ catch {
227
+ throw new Error('Arg "beats" veio como string mas não é JSON válido. Mande o array [{n, slots}].');
228
+ }
229
+ }
230
+ if (!Array.isArray(val) || val.some((b) => typeof b?.n !== "number" || !b?.slots)) {
231
+ throw new Error('Arg "beats" deve ser um array de { n: número do beat, slots: {slot: texto} }.');
232
+ }
233
+ return val;
234
+ }
194
235
  export async function pipeline(args) {
195
236
  const sessionToken = getSessionToken();
196
237
  switch (args.action) {
@@ -382,6 +423,7 @@ export async function pipeline(args) {
382
423
  ...(articleId ? { publishedArticleId: articleId } : {}),
383
424
  autoPickImages: args.autoPickImages,
384
425
  useSintetico: args.useSintetico,
426
+ ...(args.model ? { model: args.model } : {}),
385
427
  });
386
428
  }
387
429
  case "generate_carousel_production": {
@@ -393,11 +435,20 @@ export async function pipeline(args) {
393
435
  // Sinapses (reembolsa se falhar). Devolve productionId + url do editor.
394
436
  // SÍNCRONA e pesada (Gemini + imagens): vale a REGRA DO TIMEOUT — se voltar
395
437
  // Timeout, confira em get_production/list_sources antes de repetir.
438
+ //
439
+ // model trava a folha registrada; beats = copy pronto do agente (pula o
440
+ // Gemini de escrita, cobra só o picker). beats pode chegar serializado
441
+ // como string JSON (mesmo caso do payload): normaliza antes de mandar.
442
+ const beats = coerceBeatsArg(args.beats);
396
443
  return await convexAction("carouselAutofill:mcpFillCarouselFromArticle", {
397
444
  sessionToken,
398
445
  sourceId: need(args.sourceId, "sourceId"),
399
446
  autoPickImages: args.autoPickImages,
400
447
  useSintetico: args.useSintetico,
448
+ ...(args.model ? { model: args.model } : {}),
449
+ ...(beats ? { beats } : {}),
450
+ ...(args.caption ? { caption: args.caption } : {}),
451
+ ...(args.keyword ? { keyword: args.keyword } : {}),
401
452
  });
402
453
  }
403
454
  case "list_carousels":
@@ -420,18 +471,24 @@ export async function pipeline(args) {
420
471
  payload: coercePayloadArg(need(args.payload, "payload")),
421
472
  ...(args.title ? { title: args.title } : {}),
422
473
  });
423
- case "carousel_auto_images":
474
+ case "carousel_auto_images": {
424
475
  // IA escolhe imagens do banco pros slides de foto sem imagem (60 Sinapses).
476
+ // Aceita carouselId (standalone) OU productionId (carrossel da pipeline).
425
477
  // makeVisual dá ritmo a carrossel texto-pesado; generateMissing gera as que
426
478
  // o banco não cobriu (custo por imagem). SÍNCRONA e pesada com
427
479
  // generateMissing: vale a REGRA DO TIMEOUT.
480
+ if (!args.carouselId && !args.productionId) {
481
+ throw new Error("carousel_auto_images: passe carouselId (standalone) OU productionId (carrossel da pipeline).");
482
+ }
428
483
  return await convexAction("carouselAutofill:mcpCarouselAutoImages", {
429
484
  sessionToken,
430
- carouselId: need(args.carouselId, "carouselId"),
485
+ ...(args.carouselId ? { carouselId: args.carouselId } : {}),
486
+ ...(!args.carouselId && args.productionId ? { productionId: args.productionId } : {}),
431
487
  force: args.force,
432
488
  makeVisual: args.makeVisual,
433
489
  generateMissing: args.generateMissing,
434
490
  });
491
+ }
435
492
  case "carousel_generate_image":
436
493
  // Gera imagem NOVA pra UM slide (nanoBanana, ~450 Sinapses) e salva no
437
494
  // carrossel. SÍNCRONA (~20-60s): vale a REGRA DO TIMEOUT.
@@ -53,6 +53,10 @@ import { httpUrl } from "../schema.js";
53
53
  * antes de fechar no 2.0 padrão. Pedir 1080p nos dois cai em 720p
54
54
  * (cobrando 720p).
55
55
  * - sapiens-video-kling Kling 3.0 Pro — dá vida a uma imagem, 3-15s, sound opcional (i2v/t2v)
56
+ * - sapiens-video-hailuo Hailuo 2.3 (MiniMax) — física e movimento em 768p, 6 ou 10s (t2v/i2v).
57
+ * Motor puro: SEM áudio, SEM referência, SEM frame final.
58
+ * - sapiens-video-hailuo-pro Hailuo 2.3 Pro — o mesmo em 1080p, 5s fixo (duração não é param).
59
+ * É o 1080p mais barato da casa depois do Seedance 1.0 Fast.
56
60
  * - sapiens-video-wan WAN 2.5 — imagem que fala/canta (áudio+lip-sync nativo), 5/10s (i2v)
57
61
  * - sapiens-video-kling-motion Kling Motion — transfere o movimento de um vídeo pra uma imagem
58
62
  * (PRECISA de pessoa com tronco visível na imagem E no vídeo;
@@ -87,6 +91,8 @@ const VIDEO_MODELS = [
87
91
  "sapiens-video-seedance-2-fast",
88
92
  "sapiens-video-seedance-2-mini",
89
93
  "sapiens-video-kling",
94
+ "sapiens-video-hailuo",
95
+ "sapiens-video-hailuo-pro",
90
96
  "sapiens-video-wan",
91
97
  "sapiens-video-kling-motion",
92
98
  "sapiens-video-shot-mimic",
@@ -114,7 +120,21 @@ export const videoSchema = z.object({
114
120
  "film-status",
115
121
  "film-publish",
116
122
  "film-delete",
123
+ "caption-list",
124
+ "caption-generate",
125
+ "caption-translate",
117
126
  ]),
127
+ // --- Legenda (caption-*): a fala da fita virando texto, em vários idiomas ---
128
+ captionLang: z
129
+ .enum(["pt", "en", "ja", "es", "ko", "zh", "fr", "it", "de"])
130
+ .optional()
131
+ .describe("caption-generate: o idioma FALADO no vídeo. caption-translate: o idioma de DESTINO. " +
132
+ "A legenda é desenhada pela casa e trocável no play; o trio da casa é pt+en+ja."),
133
+ captionTrio: z
134
+ .boolean()
135
+ .optional()
136
+ .describe("caption-generate: true transcreve E traduz pro trio da casa (pt, en, ja) numa chamada. " +
137
+ "Cobra cada etapa por vez, então falha numa tradução não perde as que já saíram."),
118
138
  // --- Vídeos Programáticos (film-*): Lab de specs da casa, ADMIN, sem custo ---
119
139
  filmKind: z
120
140
  .enum(FILM_KINDS)
@@ -178,6 +198,7 @@ export const videoSchema = z.object({
178
198
  .optional()
179
199
  .describe("action=create: modelo de vídeo. 'sapiens-video-seedance' (cinematográfico+áudio, t2v/i2v), " +
180
200
  "'sapiens-video-kling' (anima imagem, i2v/t2v), 'sapiens-video-wan' (imagem que fala, i2v), " +
201
+ "'sapiens-video-hailuo' (MiniMax, física e movimento, 768p 6/10s) e 'sapiens-video-hailuo-pro' (o mesmo em 1080p, 5s fixo) — os dois sem áudio, sem referência e sem frame final, " +
181
202
  "'sapiens-video-kling-motion' (motion transfer, precisa pessoa na imagem E no vídeo de movimento; vídeo de referência MÁX 10s, cobra pela duração do clipe), " +
182
203
  "'sapiens-video-shot-mimic' (recria o plano do vídeo de referência com seu personagem: mesma câmera, mesmos cortes; 'driving' = previs/clipe do plano MÁX 15s, 'start' = personagem), " +
183
204
  "'sapiens-video-lite/fast/quality' (Veo 3.1), " +
@@ -411,6 +432,39 @@ export async function video(args) {
411
432
  throw new Error("film-get exige slug (descubra via film-list).");
412
433
  return await convexQuery("videoSpecs:mcpGetVideoSpec", { sessionToken, slug: args.slug });
413
434
  }
435
+ // --- Legenda ---
436
+ if (args.action === "caption-list") {
437
+ if (!args.slug)
438
+ throw new Error("caption-list exige slug (descubra via film-list).");
439
+ return await convexAction("mcpExtrasActions:mcpCaptionList", {
440
+ sessionToken,
441
+ slug: args.slug,
442
+ });
443
+ }
444
+ if (args.action === "caption-generate") {
445
+ if (!args.slug)
446
+ throw new Error("caption-generate exige slug (descubra via film-list).");
447
+ if (!args.captionLang) {
448
+ throw new Error("caption-generate exige captionLang: o idioma FALADO no vídeo (o modelo transcreve nele).");
449
+ }
450
+ return await convexAction("mcpExtrasActions:mcpCaptionGenerate", {
451
+ sessionToken,
452
+ slug: args.slug,
453
+ lang: args.captionLang,
454
+ trio: args.captionTrio,
455
+ });
456
+ }
457
+ if (args.action === "caption-translate") {
458
+ if (!args.slug)
459
+ throw new Error("caption-translate exige slug (descubra via film-list).");
460
+ if (!args.captionLang)
461
+ throw new Error("caption-translate exige captionLang (idioma de destino).");
462
+ return await convexAction("mcpExtrasActions:mcpCaptionTranslate", {
463
+ sessionToken,
464
+ slug: args.slug,
465
+ lang: args.captionLang,
466
+ });
467
+ }
414
468
  if (args.action === "film-upsert") {
415
469
  if (!args.spec || typeof args.spec !== "object") {
416
470
  throw new Error("film-upsert exige spec (objeto JSON com kind + musicMode + payload do kind: demo | aulaTour | essay | tipoMusical | dataviz).");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sapiens-mcp",
3
- "version": "1.43.4",
3
+ "version": "1.44.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": {