sapiens-mcp 1.31.0 → 1.32.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 CHANGED
@@ -86,7 +86,7 @@ const TOOLS = {
86
86
  handler: search,
87
87
  },
88
88
  sapiens_studios: {
89
- description: "Catálogo dos estúdios/experimentos Sapiens + o SEU studio + a Emancipação. Sub-actions: mine (o Meu Studio do user: nível/marca/operador/ferramentas), list (todos com URL+status+tags+mcpReady), get (detalhe de 1 slug), publishable_url (formata URL /articles/<slug>), emancipar (INICIA o Nível 3: blueprint pra construir a casa PRÓPRIA do membro na infra dele, fora do Sapiens), module (guia de um módulo de infra: fundacao/sapiens-connect/telegram/email/auth). Use quando user pergunta 'que estúdios existem', 'qual o meu studio', ou 'quero montar meu site/minha casa própria'. Estúdios cobertos: helen-voice, musicator, persona-sapiens, personagem-atlas, sapiens-shorts, sapiens-video, cena-visual, text-post-builder, comic-builder, carrosel-editorial, comunidade, repertorio.",
89
+ description: "Catálogo dos estúdios/experimentos Sapiens + o SEU studio + a Emancipação. Sub-actions: mine (o Meu Studio do user: nível/marca/operador/ferramentas), list (todos com URL+status+tags+mcpReady), get (detalhe de 1 slug), publishable_url (formata URL /articles/<slug>), emancipar (INICIA o Nível 3: blueprint pra construir a casa PRÓPRIA do membro na infra dele, fora do Sapiens), module (guia de um módulo de infra: fundacao/sapiens-connect/telegram/email/auth). Use quando user pergunta 'que estúdios existem', 'qual o meu studio', ou 'quero montar meu site/minha casa própria'. Estúdios cobertos: helen-voice, musicator, persona-sapiens, personagem-atlas, sapiens-shorts, sapiens-video, text-post-builder, comic-builder, carrosel-editorial, comunidade, repertorio.",
90
90
  schema: studiosSchema,
91
91
  handler: studios,
92
92
  },
@@ -116,7 +116,7 @@ const TOOLS = {
116
116
  handler: video,
117
117
  },
118
118
  sapiens_stock_audio: {
119
- description: "Banco de som/trilha stock (catálogo de música pronta da Helen Ailith no CDN, free/interno). Leitura pública, sem auth. Sub-actions: categories (lista os moods/usos: Ambiente & Calmo, Intenso & Dramático, Narrativo & Cabaret, Hino & Épico), list (busca faixas com filtros mood/albumSlug/durationMax/search — devolve {count, items} com title/album/url/durationSeconds/mood/tags), get (1 faixa por audioId). Use pra puxar trilha pronta em vez de gerar via Musicator: pega a `url` da faixa escolhida e usa direto no ffmpeg como BGM de /sapiens:movie, trilha de /sapiens:movie-clip ou fundo de short. Mood disponíveis: calmo, intenso, narrativo, épico, sombrio.",
119
+ description: "Banco de som da casa: trilha pronta E efeito sonoro (tabela stockAudio). Sub-actions de leitura (públicas, sem auth): categories (lista os moods/usos), list (busca com filtros mood/albumSlug/durationMax/search; kind='sfx' traz os EFEITOS: whoosh, clique, impacto, ambiência, foley — devolve {count, items} com title/url/durationSeconds/tags), get (1 item por audioId). Use pra puxar trilha/efeito pronto: pega a `url` e usa direto no ffmpeg. Não achou o efeito? generate (COBRA Sinapses, exige login) cria um novo por texto: prompt + durationSeconds (1-15, default 5) + provider ('mirelo' padrão 30 Sinapses/s mín 60 | 'elevenlabs' premium 60/s mín 120), assíncrono — acompanhe com generation-status (generationId) até 'ready' (audioUrl; o efeito também entra no acervo kind=sfx) ou 'failed' (Sinapses reembolsadas). Efeito é CURTO (1-15s): música/trilha nova é no sapiens_musicator. Mood disponíveis: calmo, intenso, narrativo, épico, sombrio.",
120
120
  schema: stockAudioSchema,
121
121
  handler: stockAudio,
122
122
  },
@@ -205,6 +205,10 @@ Música (sapiens_musicator) é fluxo de 4 passos, EM ORDEM:
205
205
  4. get passe o trackId e fique polando o status até 'ready' (ou 'failed').
206
206
  Pular pro lyrics/render sem create, ou sem os campos, sempre falha.
207
207
 
208
+ Efeito sonoro (sapiens_stock_audio): primeiro procure pronto (action=list kind=sfx, grátis). Não achou, action=generate: prompt + durationSeconds (1-15, default 5) + provider ('mirelo' padrão | 'elevenlabs' premium, custo por segundo). Assíncrono: devolve generationId, acompanhe com action=generation-status até 'ready' (audioUrl) ou 'failed' (reembolsa sozinho). Efeito é CURTO; música inteira é no sapiens_musicator, não aqui.
209
+
210
+ Sonorizar clipe (sapiens_video action=sonorize): dá som a um vídeo SEU já gerado (status completed). Passe imageId + prompt descrevendo o som da cena (ambiente, materiais, impactos); sai uma VARIANTE nova com trilha sincronizada ao movimento (MMAudio, 20 Sinapses/s do clipe, mín 100), o original fica intacto. Acompanhe com action=status no imageId NOVO que o sonorize devolve. Não re-sonorize uma variante: sonorize sempre o original.
211
+
208
212
  Vídeo (sapiens_video) action=create EXIGE 'model': sapiens-video-seedance | kling | wan | kling-motion | lite | fast | quality | omni. Sem model, falha de cara. O 'omni' (Gemini Omni) gera clipe de 10s 720p com áudio nativo a partir de TEXTO (não aceita imagem/vídeo do user, ignora duração/resolução) e EDITA os próprios vídeos: passe editOfImageId com o imageId de um vídeo Omni já gerado e o prompt vira instrução de edição sobre a MESMA cena (troca item/personagem, preserva câmera e ambiente; cada edição debita como geração nova). É o caminho pra variações com continuidade: gera a base uma vez, edita N vezes. Vídeo é o mais caro: confirme com o usuário antes. action=shadows (ADMIN, 200 Sinapses/segundo, refund na falha): passa videoUrl (URL pública) + title (+ durationSec se souber, pra cobrar proporcional; sem ela, flat ~2000) e o servidor extrai a SOMBRA (mapa de profundidade) do vídeo e guarda no Acervo (Corpo) como deepshadow reutilizável (driving pro Shot Mimic/Kling Motion). Vitrine dos demos (sem custo): sapiens_video action=demos lista seus demo films com slug; action=showcase (slug + showcase=true/false + showcaseTag + showcaseOrder) cura o mini-cinema da /conectar-claude. FILMES DA CASA (Estúdio de Vídeo, tela /experimentos/films): são OUTRA coisa, três kinds na tabela videoSpecs: demo (UI clonada + câmera), aula-tour (telas reais + narração) e essay (fita-ensaio abstrata: partícula/tinta vira símbolo, com a logo explodindo no fim; narração da casa OPCIONAL, sai em 16:9 ou 9:16 vertical, e o filme final é limpo, sem rótulos de produção). Eles NÃO são gerados por este MCP: o user monta o spec na tela e um agente local (Claude Code, skill /film) produz o mp4 no repo da casa. Se o user pedir 'fita-ensaio' ou 'demo film' aqui, aponte pra tela e pro agente local.
209
213
 
210
214
  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. PROMPT na regra da casa: full-bleed, sujeito oversized (70%+ do frame), sem "tarot card"/"intimate scale"/moldura/margem. action=models (sem custo, sem login) lista os modelos ativos com o preço atual: consulte quando não tiver certeza do modelo ou do custo.
@@ -237,7 +241,7 @@ Voz da casa: 1ª pessoa, direto, anti-corporate, sem travessão. Pra bom entende
237
241
  // escreve/cobra (as multi-action com 1 mutation ficam de fora, honestamente).
238
242
  const READ_ONLY_TOOLS = new Set([
239
243
  "sapiens_search",
240
- "sapiens_stock_audio",
244
+ // sapiens_stock_audio SAIU do set: action=generate cobra Sinapses.
241
245
  "sapiens_stock_video",
242
246
  "sapiens_atlas",
243
247
  "sapiens_reference",
@@ -43,7 +43,19 @@ export async function aula(args) {
43
43
  }
44
44
  case "upsert": {
45
45
  const slug = need(args.slug, "slug");
46
- const aulaObj = need(args.aula, "aula");
46
+ const raw = need(args.aula, "aula");
47
+ // `aula` é z.any(): sem tipo declarado, o cliente costuma serializar o
48
+ // objeto como string JSON. Normaliza aqui pra mandar objeto sempre (o
49
+ // servidor também parseia string, mas o shape certo sai daqui).
50
+ let aulaObj = raw;
51
+ if (typeof aulaObj === "string") {
52
+ try {
53
+ aulaObj = JSON.parse(aulaObj);
54
+ }
55
+ catch {
56
+ throw new Error('Arg "aula" veio como string mas não é JSON válido. Mande o objeto da aula.');
57
+ }
58
+ }
47
59
  return await convexMutation("mcpExtras:mcpUpsertAula", {
48
60
  sessionToken,
49
61
  slug,
@@ -24,7 +24,7 @@ export const helenSchema = z.object({
24
24
  modelId: z
25
25
  .string()
26
26
  .optional()
27
- .describe("ElevenLabs: 'eleven_v3' (default) ou 'eleven_multilingual_v2'. Google: 'gemini-2.5-flash-preview-tts'."),
27
+ .describe("ElevenLabs: 'eleven_v3' (default) ou 'eleven_multilingual_v2'. Google: 'gemini-3.1-flash-tts-preview' (default)."),
28
28
  voiceId: z
29
29
  .string()
30
30
  .optional()
@@ -71,7 +71,7 @@ const PRESETS = {
71
71
  ],
72
72
  },
73
73
  google: {
74
- note: "Google Gemini TTS. modelId 'gemini-2.5-flash-preview-tts'. Custa ~$0.01 USD pra 500 chars (mais barato).",
74
+ note: "Google Gemini TTS. modelId 'gemini-3.1-flash-tts-preview'. Custa ~$0.01 USD pra 500 chars (mais barato).",
75
75
  voices: [
76
76
  { voiceName: "Kore", use: "feminina, neutra (default)" },
77
77
  { voiceName: "Aoede", use: "feminina, melódica" },
@@ -98,7 +98,9 @@ export async function helen(args) {
98
98
  const modelId = args.modelId ??
99
99
  (provider === "elevenlabs"
100
100
  ? "eleven_v3"
101
- : "gemini-2.5-flash-preview-tts");
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 só sai na conta central).
103
+ : "gemini-3.1-flash-tts-preview");
102
104
  if (provider === "elevenlabs" && !args.voiceId) {
103
105
  throw new Error("provider=elevenlabs exige voiceId. Use action=list_presets pra ver opções.");
104
106
  }
@@ -84,16 +84,8 @@ const FORMAT_GUIDE = {
84
84
  scenes: [{ visual: "string", voiceover: "string", duration: "number" }],
85
85
  },
86
86
  },
87
- cena_visual: {
88
- description: "Vídeo curto programático via Hyperframes (movimento + texto entram por código).",
89
- payloadHint: {
90
- concept: "string (ideia da cena)",
91
- scenes: [{ text: "string", motion: "string", durationMs: "number" }],
92
- durationMs: "number total opcional",
93
- },
94
- },
95
87
  video_yt: {
96
- description: "Vídeo programático longer-form.",
88
+ description: "Vídeo longer-form.",
97
89
  payloadHint: {
98
90
  title: "string",
99
91
  script: "string roteiro completo",
@@ -10,14 +10,14 @@ const FORMATS = [
10
10
  "post_social",
11
11
  "mega_grafico",
12
12
  "shorts_yt",
13
- "cena_visual",
14
13
  "video_yt",
15
14
  "musica",
16
15
  "aula",
17
16
  ];
18
17
  // Formatos legados: fora do FORMAT_GUIDE (não advertidos), mas ainda aceitos na
19
- // validação pra editar productions antigas sem quebrar.
20
- const LEGACY_FORMATS = ["post_linkedin", "post_twitter", "post_threads"];
18
+ // validação pra editar productions antigas sem quebrar. cena_visual ("Vídeo
19
+ // Programático") saiu de linha em jul/2026: Films + gerador de vídeo cobrem o caso.
20
+ const LEGACY_FORMATS = ["post_linkedin", "post_twitter", "post_threads", "cena_visual"];
21
21
  const ACCEPTED_FORMATS = [...FORMATS, ...LEGACY_FORMATS];
22
22
  const STATUSES = ["draft", "ready", "finalized"];
23
23
  export const pipelineSchema = z.object({
@@ -136,6 +136,20 @@ function need(value, name) {
136
136
  }
137
137
  return value;
138
138
  }
139
+ // `payload` é z.any(): sem tipo declarado, o cliente costuma serializar o
140
+ // objeto como string JSON (mesmo caso do upsert de aula em aula.ts). Normaliza
141
+ // aqui pra mandar objeto sempre (o servidor também parseia string por defesa,
142
+ // mas o shape certo sai daqui). String crua no banco = editor abre "vazio".
143
+ function coercePayloadArg(raw) {
144
+ if (typeof raw !== "string")
145
+ return raw;
146
+ try {
147
+ return JSON.parse(raw);
148
+ }
149
+ catch {
150
+ throw new Error('Arg "payload" veio como string mas não é JSON válido. Mande o objeto do payload.');
151
+ }
152
+ }
139
153
  export async function pipeline(args) {
140
154
  const sessionToken = getSessionToken();
141
155
  switch (args.action) {
@@ -190,7 +204,7 @@ export async function pipeline(args) {
190
204
  sessionToken,
191
205
  sourceId: need(args.sourceId, "sourceId"),
192
206
  format: need(args.format, "format"),
193
- payload: args.payload,
207
+ payload: coercePayloadArg(args.payload),
194
208
  });
195
209
  return { productionId: id };
196
210
  }
@@ -198,7 +212,7 @@ export async function pipeline(args) {
198
212
  await convexMutation("pipelineMcp:mcpUpdateProductionPayload", {
199
213
  sessionToken,
200
214
  productionId: need(args.productionId, "productionId"),
201
- payload: need(args.payload, "payload"),
215
+ payload: coercePayloadArg(need(args.payload, "payload")),
202
216
  status: args.status,
203
217
  });
204
218
  return { ok: true };
@@ -1,19 +1,26 @@
1
1
  import { z } from "zod";
2
- import { convexQuery } from "../convexClient.js";
2
+ import { convexQuery, convexAction, getSessionToken } from "../convexClient.js";
3
3
  /**
4
- * Banco de som/trilha stock (tabela stockAudio). Catálogo de música pronta pra
5
- * usar como trilha em movie/movie-clip/shorts. Leitura pública (acervo free),
6
- * sem auth. Espelha o padrão do stock de imagem.
4
+ * Banco de som da casa (tabela stockAudio): trilha pronta E efeito sonoro.
5
+ * Leitura pública (acervo free, sem auth); geração de efeito cobra Sinapses
6
+ * (exige login). Espelha o padrão do stock de imagem.
7
7
  *
8
8
  * Fluxo típico numa skill de vídeo:
9
9
  * 1. action=categories -> ver os moods/usos disponíveis
10
- * 2. action=list (mood=.. durationMax=..) -> achar candidatas
11
- * 3. pega a `url` da escolhida -> usa direto no ffmpeg como trilha
10
+ * 2. action=list (mood=.. durationMax=.. | kind=sfx) -> achar candidatas
11
+ * 3. pega a `url` da escolhida -> usa direto no ffmpeg como trilha/efeito
12
+ *
13
+ * Não achou o efeito? action=generate cria um novo (assíncrono):
14
+ * generate (prompt + durationSeconds + provider) -> generationId
15
+ * generation-status (generationId) até 'ready' -> audioUrl
16
+ * O efeito pronto também entra no acervo (action=list kind=sfx).
12
17
  */
13
18
  export const stockAudioSchema = z.object({
14
19
  action: z
15
- .enum(["list", "get", "categories"])
16
- .describe("list = busca faixas; get = 1 faixa por id; categories = moods/usos."),
20
+ .enum(["list", "get", "categories", "generate", "generation-status"])
21
+ .describe("list = busca faixas/efeitos; get = 1 item por id; categories = moods/usos; " +
22
+ "generate = cria efeito sonoro novo (COBRA Sinapses, assíncrono); " +
23
+ "generation-status = status de uma geração (até ready/failed)."),
17
24
  limit: z
18
25
  .number()
19
26
  .int()
@@ -42,7 +49,29 @@ export const stockAudioSchema = z.object({
42
49
  .positive()
43
50
  .optional()
44
51
  .describe("Duração máxima em segundos (ex: 120 pra trilha de movie < 2min)."),
52
+ kind: z
53
+ .enum(["music", "sfx", "all"])
54
+ .optional()
55
+ .describe("action=list: 'music' (default, trilhas) | 'sfx' (efeitos sonoros: whoosh, clique, ambiência, foley) | 'all'."),
45
56
  audioId: z.string().optional().describe("stockAudio:_id (obrigatório pra action=get)."),
57
+ // --- action=generate (efeito sonoro novo) ---
58
+ prompt: z
59
+ .string()
60
+ .optional()
61
+ .describe("action=generate: descreve o efeito (ex: 'whoosh curto de transição, grave e limpo'). " +
62
+ "Mín. 4 chars. Efeito é CURTO (1-15s); música/trilha é no sapiens_musicator."),
63
+ durationSeconds: z
64
+ .number()
65
+ .optional()
66
+ .describe("action=generate: duração alvo em segundos, 1 a 15. Default 5. O custo escala por segundo."),
67
+ provider: z
68
+ .enum(["mirelo", "elevenlabs"])
69
+ .optional()
70
+ .describe("action=generate: 'mirelo' (padrão, 30 Sinapses/s, mín 60) | 'elevenlabs' (premium, 60/s, mín 120)."),
71
+ generationId: z
72
+ .string()
73
+ .optional()
74
+ .describe("action=generation-status: o generationId que o generate devolveu."),
46
75
  });
47
76
  export async function stockAudio(args) {
48
77
  if (args.action === "categories") {
@@ -54,6 +83,28 @@ export async function stockAudio(args) {
54
83
  throw new Error("action=get exige audioId.");
55
84
  return await convexQuery("stockAudio:getAudioById", { id: args.audioId });
56
85
  }
86
+ if (args.action === "generate") {
87
+ if (!args.prompt || args.prompt.trim().length < 4) {
88
+ throw new Error("action=generate exige prompt (mín. 4 chars descrevendo o efeito).");
89
+ }
90
+ const sessionToken = getSessionToken();
91
+ return await convexAction("mcpExtrasActions:mcpStockAudioGenerate", {
92
+ sessionToken,
93
+ prompt: args.prompt,
94
+ durationSeconds: args.durationSeconds,
95
+ provider: args.provider,
96
+ });
97
+ }
98
+ if (args.action === "generation-status") {
99
+ if (!args.generationId) {
100
+ throw new Error("action=generation-status exige generationId (o que o generate devolveu).");
101
+ }
102
+ const sessionToken = getSessionToken();
103
+ return await convexQuery("mcpExtras:mcpSfxStatus", {
104
+ sessionToken,
105
+ generationId: args.generationId,
106
+ });
107
+ }
57
108
  // action=list
58
109
  return await convexQuery("stockAudio:getAudio", {
59
110
  limit: args.limit ?? 20,
@@ -62,5 +113,6 @@ export async function stockAudio(args) {
62
113
  mood: args.mood,
63
114
  albumSlug: args.albumSlug,
64
115
  durationMax: args.durationMax,
116
+ kind: args.kind,
65
117
  });
66
118
  }
@@ -88,7 +88,7 @@ const STUDIOS = {
88
88
  },
89
89
  "sapiens-video": {
90
90
  name: "Sapiens Video (longer-form)",
91
- description: "Vídeo programático longer-form com prompt livre. v1.5: generate via MCP.",
91
+ description: "Vídeo longer-form com prompt livre. v1.5: generate via MCP.",
92
92
  url: `${APP}/imgen-sapiens-video`,
93
93
  status: "beta",
94
94
  tags: ["video", "longform", "voiceover", "veo"],
@@ -96,15 +96,6 @@ const STUDIOS = {
96
96
  mcpReady: true,
97
97
  mcpNote: "Coberto via sapiens_video (generate). Aspect configurável, references opcionais (start/end frames).",
98
98
  },
99
- "cena-visual": {
100
- name: "Sapiens Cena Visual",
101
- description: "Still única pra abertura, ou hero shot, ou simbólica.",
102
- url: `${APP}/imgen-sapiens-cena-visual`,
103
- status: "stable",
104
- tags: ["image", "still", "hero"],
105
- convex: "sapiensCenaVisualActions",
106
- mcpReady: false,
107
- },
108
99
  "text-post-builder": {
109
100
  name: "Text Post Builder",
110
101
  description: "Gerador de post de texto puro pra rede social (Instagram, LinkedIn, Twitter). Inclui geração de imagem opcional acoplada.",
@@ -21,6 +21,9 @@ import { httpUrl } from "../schema.js";
21
21
  * com as duas views por ficha: url = sombra pura (driving pros
22
22
  * geradores) e skeletonUrl = a soma (sombras + esqueleto, preview
23
23
  * humano que separa personagem de objeto). Sem custo.
24
+ * - sonorize: dá som a um clipe SEU já gerado (MMAudio v2): imageId + prompt do
25
+ * som da cena -> VARIANTE nova com trilha sincronizada (o original
26
+ * fica intacto). Cobra por segundo do clipe; poll via action=status.
24
27
  *
25
28
  * Modelos (action=create):
26
29
  * - sapiens-video-seedance Seedance 2.0 — cena com áudio nativo, 4-15s, 480/720/1080p (t2v/i2v)
@@ -54,7 +57,7 @@ const VIDEO_MODELS = [
54
57
  "sapiens-video-omni",
55
58
  ];
56
59
  export const videoSchema = z.object({
57
- action: z.enum(["create", "generate", "status", "models", "demos", "showcase", "shadows", "shadows-list"]),
60
+ action: z.enum(["create", "generate", "status", "models", "demos", "showcase", "shadows", "shadows-list", "sonorize"]),
58
61
  // --- action=shadows (deepshadows: extrai o mapa de profundidade de um vídeo) ---
59
62
  videoUrl: httpUrl()
60
63
  .optional()
@@ -121,7 +124,8 @@ export const videoSchema = z.object({
121
124
  .string()
122
125
  .optional()
123
126
  .describe("action=generate ou action=status: generatedImages:_id do vídeo. O create devolve o imageId; " +
124
- "ou um vídeo já criado no site (o row define modelo + custo). Use sapiens_gallery action=list pra descobrir."),
127
+ "ou um vídeo já criado no site (o row define modelo + custo). Use sapiens_gallery action=list pra descobrir. " +
128
+ "action=sonorize: o imageId do clipe SEU (status completed) que vai ganhar som."),
125
129
  // --- comum ---
126
130
  prompt: z
127
131
  .string()
@@ -185,7 +189,7 @@ export async function video(args) {
185
189
  count: models.length,
186
190
  default: "sapiens-video-seedance",
187
191
  models,
188
- note: "basePriceSinapses é o PISO (config mais barata, já com override admin); o preço real escala por duração x resolução [x áudio]. available=false = 'em breve' (depende de env, ex: Omni). O 'sapiens-cena-visual' (Hyperframes) não aparece: é render local, fora deste MCP.",
192
+ note: "basePriceSinapses é o PISO (config mais barata, já com override admin); o preço real escala por duração x resolução [x áudio]. available=false = 'em breve' (depende de env, ex: Omni).",
189
193
  };
190
194
  }
191
195
  const sessionToken = getSessionToken();
@@ -216,6 +220,19 @@ export async function video(args) {
216
220
  imageId: args.imageId,
217
221
  });
218
222
  }
223
+ if (args.action === "sonorize") {
224
+ if (!args.imageId) {
225
+ throw new Error("action=sonorize exige imageId (o clipe SEU, já completed, que vai ganhar som).");
226
+ }
227
+ if (!args.prompt || args.prompt.trim().length < 4) {
228
+ throw new Error("action=sonorize exige prompt descrevendo o som da cena (ambiente, materiais, impactos, textura).");
229
+ }
230
+ return await convexAction("mcpExtrasActions:mcpVideoSonorize", {
231
+ sessionToken,
232
+ sourceImageId: args.imageId,
233
+ prompt: args.prompt,
234
+ });
235
+ }
219
236
  if (args.action === "create") {
220
237
  if (!args.model) {
221
238
  throw new Error("action=create exige model (ex: sapiens-video-seedance, sapiens-video-kling, sapiens-video-wan, sapiens-video-kling-motion).");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sapiens-mcp",
3
- "version": "1.31.0",
3
+ "version": "1.32.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": {