sapiens-mcp 1.23.3 → 1.23.4

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
@@ -20,6 +20,7 @@ import { shorts, shortsSchema } from "./tools/shorts.js";
20
20
  import { video, videoSchema } from "./tools/video.js";
21
21
  import { write, writeSchema } from "./tools/write.js";
22
22
  import { stockAudio, stockAudioSchema } from "./tools/stockAudio.js";
23
+ import { stockVideo, stockVideoSchema } from "./tools/stockVideo.js";
23
24
  import { brand, brandSchema } from "./tools/brand.js";
24
25
  import { character, characterSchema } from "./tools/character.js";
25
26
  import { profile, profileSchema } from "./tools/profile.js";
@@ -116,6 +117,11 @@ const TOOLS = {
116
117
  schema: stockAudioSchema,
117
118
  handler: stockAudio,
118
119
  },
120
+ sapiens_stock_video: {
121
+ description: "Banco de clipe/B-roll stock (catálogo de vídeo curto pronto no CDN, baixável, free/interno). Leitura pública, sem auth. Sub-actions: categories (lista moods/usos: Ambiente & Atmosfera, Abertura & Fechamento, Textura & Abstrato, Natureza & Paisagem, Tech & Cripto), list (busca clipes com filtros mood/orientation/loopOnly/durationMax/search — devolve {count, items} com title/url/posterUrl/durationSeconds/orientation/loopFriendly/tags), get (1 clipe por videoId). Use pra puxar B-roll pronto em vez de gerar via sapiens_video: pega a `url` do clipe e usa como fundo/atmosfera de página, B-roll no ffmpeg de /sapiens:movie, ou frame de começo/fim no criar vídeo (orientation vertical=9:16 pra short, horizontal=16:9, loopOnly=true pra fundo que repete limpo).",
122
+ schema: stockVideoSchema,
123
+ handler: stockVideo,
124
+ },
119
125
  sapiens_brand: {
120
126
  description: "Brand Sapiens (= Design System) — paleta + tipografia + voz + estilo de imagem + persona como FONTE ÚNICA de estilo (espelha o Estúdio de Brand do app). Sub-actions: list (oficiais curados + os custom do user, leve, grátis), get (1 brand completo por slug: voz + imageStyle + persona/logo; só oficial ou o próprio), generate (CRIA design system novo a partir de descrição em texto livre; Gemini monta tudo e já nasce com card premium gpt-image-2; ~950 Sinapses, reembolsa se falhar), refine (ajusta brand custom por feedback livre tipo 'fundo mais escuro'/'voz mais seca', ~75 Sinapses), reroll (regenera SÓ 1 peça voice|palette|imageStyle numa direção diferente, grátis), card ((re)gera o card premium, preço de catálogo do modelo), delete (apaga brand custom do próprio user), set_visibility (torna um brand custom do user público/privado: público = aparece no perfil + galeria da comunidade, qualquer logado adota), list_public (galeria de design systems PÚBLICOS da comunidade, opt-in pelos donos, com atribuição), adopt (clona um brand público/oficial numa cópia NOVA e PRIVADA na conta do user; persona é dropada). Qualquer conta logada; Sinapses saem do dono do sessionToken. Pra generate: converse com o user e monte uma description rica (vibe, cores, voz, públicos, refs; se ele colar amostra de texto dele, inclua pra a voz sair dali) ANTES de chamar. Confirme o custo (~950 Sinapses) antes de gerar.",
121
127
  schema: brandSchema,
@@ -200,7 +206,7 @@ Tirinha / Comic (sapiens_pipeline format=tirinha + sapiens_image): fluxo de 2 FA
200
206
  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.
201
207
 
202
208
  Voz da casa: 1ª pessoa, direto, anti-corporate, sem travessão. Pra bom entendedor, meia palavra basta.`;
203
- const server = new Server({ name: "mcp-sapiens", version: "1.23.3" }, { capabilities: { tools: {} }, instructions: SAPIENS_INSTRUCTIONS });
209
+ const server = new Server({ name: "mcp-sapiens", version: "1.23.4" }, { capabilities: { tools: {} }, instructions: SAPIENS_INSTRUCTIONS });
204
210
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
205
211
  tools: Object.entries(TOOLS).map(([name, t]) => ({
206
212
  name,
@@ -233,4 +239,4 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
233
239
  });
234
240
  const transport = new StdioServerTransport();
235
241
  await server.connect(transport);
236
- console.error("mcp-sapiens v1.23.3 rodando via stdio (26 tools)");
242
+ console.error("mcp-sapiens v1.23.4 rodando via stdio (26 tools)");
@@ -11,7 +11,8 @@ import { convexAction, getSessionToken } from "../convexClient.js";
11
11
  * - history imagens recentes do user (privadas + públicas)
12
12
  * - favorites imagens que o user curtiu (só as suas)
13
13
  * - videos vídeos do user (Meus Vídeos)
14
- * - acervo stock + comunidade (público); aceita `term` (busca) e `source`
14
+ * - stock_video Banco de Vídeo da casa (clipes/B-roll); aceita `term`/`orientation`/`loopOnly`
15
+ * - acervo stock + comunidade de IMAGEM (público); aceita `term` (busca) e `source`
15
16
  * - characters personagens; `mode` = 'mine' (default) ou 'public'
16
17
  *
17
18
  * Cada item volta normalizado:
@@ -26,9 +27,10 @@ export const referenceSchema = z.object({
26
27
  .enum(["browse"])
27
28
  .describe("Só 'browse' por enquanto: navega um bucket do acervo."),
28
29
  bucket: z
29
- .enum(["history", "favorites", "videos", "acervo", "characters"])
30
+ .enum(["history", "favorites", "videos", "stock_video", "acervo", "characters"])
30
31
  .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
+ "'videos' (seus vídeos), 'stock_video' (Banco de Vídeo da casa: clipes/B-roll prontos, aceita term/orientation/loopOnly), " +
33
+ "'acervo' (stock + comunidade públicos de IMAGEM), 'characters' (personagens)."),
32
34
  page: z
33
35
  .number()
34
36
  .int()
@@ -45,11 +47,19 @@ export const referenceSchema = z.object({
45
47
  term: z
46
48
  .string()
47
49
  .optional()
48
- .describe(" bucket 'acervo': busca por texto (prompt/título/tag). Ex: 'house'."),
50
+ .describe("Buckets 'acervo' e 'stock_video': busca por texto (prompt/título/tag/mood). Ex: 'chuva'."),
49
51
  source: z
50
52
  .enum(["all", "stock", "community"])
51
53
  .optional()
52
54
  .describe("Só bucket 'acervo': fonte. Default 'all' (stock + comunidade)."),
55
+ orientation: z
56
+ .enum(["vertical", "horizontal", "square"])
57
+ .optional()
58
+ .describe("Só bucket 'stock_video': filtra formato (vertical 9:16, horizontal 16:9, square 1:1)."),
59
+ loopOnly: z
60
+ .boolean()
61
+ .optional()
62
+ .describe("Só bucket 'stock_video': só clipes loopFriendly (loop limpo, bom pra fundo)."),
53
63
  mode: z
54
64
  .enum(["mine", "public"])
55
65
  .optional()
@@ -65,6 +75,8 @@ export async function reference(args) {
65
75
  limit: args.limit,
66
76
  term: args.term,
67
77
  source: args.source,
78
+ orientation: args.orientation,
79
+ loopOnly: args.loopOnly,
68
80
  mode: args.mode,
69
81
  });
70
82
  let note;
@@ -72,6 +84,10 @@ export async function reference(args) {
72
84
  note =
73
85
  "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
86
  }
87
+ else if (args.bucket === "stock_video") {
88
+ note =
89
+ "Clipes do Banco de Vídeo da casa (B-roll pronto). Use a `url` do clipe: como referência de vídeo em sapiens_video (mesma rail do Meus Vídeos), de fundo/atmosfera, ou baixe direto. orientation/durationSeconds/loopFriendly ajudam a escolher. Não são imagem: NÃO servem de frame inicial/final (pra frame use uma IMAGEM).";
90
+ }
75
91
  else if (args.bucket === "acervo" || args.bucket === "characters") {
76
92
  note =
77
93
  "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.";
@@ -0,0 +1,72 @@
1
+ import { z } from "zod";
2
+ import { convexQuery } from "../convexClient.js";
3
+ /**
4
+ * Banco de clipe/B-roll stock (tabela stockVideo). Catálogo de vídeo curto pronto
5
+ * pra usar: fundo/atmosfera de página, B-roll que a IA escolhe pela cena, e clipe
6
+ * de começo/fim no criar vídeo. Acervo BAIXÁVEL (mp4 direto do Bunny CDN, não
7
+ * stream). Leitura pública, sem auth. Espelha o stock de som.
8
+ *
9
+ * Fluxo típico:
10
+ * 1. action=categories -> ver os moods/usos disponíveis
11
+ * 2. action=list (mood=.. orientation=.. loopOnly=.. durationMax=..) -> candidatos
12
+ * 3. pega a `url` do escolhido -> usa direto (fundo, B-roll no ffmpeg, start/end)
13
+ */
14
+ export const stockVideoSchema = z.object({
15
+ action: z
16
+ .enum(["list", "get", "categories"])
17
+ .describe("list = busca clipes; get = 1 clipe por id; categories = moods/usos."),
18
+ limit: z
19
+ .number()
20
+ .int()
21
+ .positive()
22
+ .max(100)
23
+ .optional()
24
+ .describe("Default 20, max 100. Só action=list."),
25
+ categoryId: z
26
+ .string()
27
+ .optional()
28
+ .describe("stockVideoCategories:_id pra filtrar por mood/uso (action=list)."),
29
+ search: z
30
+ .string()
31
+ .optional()
32
+ .describe("Busca em título/mood/tags/descrição (action=list)."),
33
+ mood: z
34
+ .string()
35
+ .optional()
36
+ .describe("Filtra por mood exato (action=list)."),
37
+ orientation: z
38
+ .enum(["vertical", "horizontal", "square"])
39
+ .optional()
40
+ .describe("Filtra por formato: vertical (9:16), horizontal (16:9), square (1:1)."),
41
+ loopOnly: z
42
+ .boolean()
43
+ .optional()
44
+ .describe("true = só clipes loopFriendly (loop limpo, bom pra fundo de página)."),
45
+ durationMax: z
46
+ .number()
47
+ .positive()
48
+ .optional()
49
+ .describe("Duração máxima em segundos (ex: 8 pra fundo curto / clipe de começo)."),
50
+ videoId: z.string().optional().describe("stockVideo:_id (obrigatório pra action=get)."),
51
+ });
52
+ export async function stockVideo(args) {
53
+ if (args.action === "categories") {
54
+ const cats = await convexQuery("stockVideo:getCategories", {});
55
+ return { count: cats?.length ?? 0, categories: cats ?? [] };
56
+ }
57
+ if (args.action === "get") {
58
+ if (!args.videoId)
59
+ throw new Error("action=get exige videoId.");
60
+ return await convexQuery("stockVideo:getVideoById", { id: args.videoId });
61
+ }
62
+ // action=list
63
+ return await convexQuery("stockVideo:getVideos", {
64
+ limit: args.limit ?? 20,
65
+ categoryId: args.categoryId,
66
+ search: args.search,
67
+ mood: args.mood,
68
+ orientation: args.orientation,
69
+ loopOnly: args.loopOnly,
70
+ durationMax: args.durationMax,
71
+ });
72
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sapiens-mcp",
3
- "version": "1.23.3",
3
+ "version": "1.23.4",
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": {