sapiens-mcp 1.23.2 → 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 +9 -3
- package/dist/tools/reference.js +20 -4
- package/dist/tools/stockVideo.js +72 -0
- package/dist/tools/write.js +10 -0
- package/package.json +1 -1
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";
|
|
@@ -67,7 +68,7 @@ const TOOLS = {
|
|
|
67
68
|
handler: article,
|
|
68
69
|
},
|
|
69
70
|
sapiens_write: {
|
|
70
|
-
description: "Artigos self-serve do PRÓPRIO usuário (qualquer conta logada, não só admin) — espaço pessoal, aparece em /u/<username>, NÃO é o blog editorial. Sub-actions: generate (gera 1 artigo na voz Sapiens a partir de brief livre, ou reescrevendo um artigo publicado/texto teu; custa 400 Sinapses, reembolsa se falhar; salva como rascunho), list (teus artigos), get (1 artigo teu por id, corpo completo), update (edita title/content/excerpt/tldr), publish (publish=true publica no teu perfil, false volta pra rascunho). Identidade vem do sessionToken; cobra as Sinapses do dono do token. Pra blog editorial curado (owner-only) use sapiens_article.",
|
|
71
|
+
description: "Artigos self-serve do PRÓPRIO usuário (qualquer conta logada, não só admin) — espaço pessoal, aparece em /u/<username>, NÃO é o blog editorial. Sub-actions: generate (gera 1 artigo na voz Sapiens a partir de brief livre, ou reescrevendo um artigo publicado/texto teu; custa 400 Sinapses, reembolsa se falhar; salva como rascunho. Capa: por padrão gera uma capa-cortesia grátis; se você JÁ tem a imagem (gerou via sapiens_image, ou o artigo é sobre ela), passe coverImageId (id da tua galeria) ou coverImageUrl (host Sapiens) pra ELA virar a capa em vez da cortesia), list (teus artigos), get (1 artigo teu por id, corpo completo), update (edita title/content/excerpt/tldr), publish (publish=true publica no teu perfil, false volta pra rascunho). Identidade vem do sessionToken; cobra as Sinapses do dono do token. Pra blog editorial curado (owner-only) use sapiens_article.",
|
|
71
72
|
schema: writeSchema,
|
|
72
73
|
handler: write,
|
|
73
74
|
},
|
|
@@ -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.
|
|
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.
|
|
242
|
+
console.error("mcp-sapiens v1.23.4 rodando via stdio (26 tools)");
|
package/dist/tools/reference.js
CHANGED
|
@@ -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
|
-
* -
|
|
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), '
|
|
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("
|
|
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/dist/tools/write.js
CHANGED
|
@@ -48,6 +48,14 @@ export const writeSchema = z.object({
|
|
|
48
48
|
.max(5)
|
|
49
49
|
.optional()
|
|
50
50
|
.describe("Até 5 ids de obras do Repertório do usuário (repertorioItems:_id) pra IA usar como lente/referência do texto (sinopse + nota do dono entram no prompt). Ache os ids via sapiens_repertorio (action=list/search). Só do próprio usuário; ids de outros são ignorados."),
|
|
51
|
+
coverImageId: z
|
|
52
|
+
.string()
|
|
53
|
+
.optional()
|
|
54
|
+
.describe("Capa PRONTA (opcional, só generate): id de uma imagem da TUA galeria (generatedImages:_id, ache via sapiens_gallery/sapiens_reference) que vira a capa do artigo em vez da capa-cortesia gerada do zero. Use quando você JÁ gerou a imagem (sapiens_image) e o artigo é sobre ela — assim a peça não nasce sem capa se a cortesia falhar."),
|
|
55
|
+
coverImageUrl: z
|
|
56
|
+
.string()
|
|
57
|
+
.optional()
|
|
58
|
+
.describe("Alternativa a coverImageId: URL de imagem do Sapiens (Bunny CDN / Convex) pra usar como capa pronta. Host fora da allowlist é recusado."),
|
|
51
59
|
// --- list ---
|
|
52
60
|
limit: z
|
|
53
61
|
.number()
|
|
@@ -98,6 +106,8 @@ export async function write(args) {
|
|
|
98
106
|
voiceStyle: args.voiceStyle,
|
|
99
107
|
customVoice: args.customVoice,
|
|
100
108
|
repertorioItemIds: args.repertorioItemIds,
|
|
109
|
+
coverImageId: args.coverImageId,
|
|
110
|
+
coverImageUrl: args.coverImageUrl,
|
|
101
111
|
});
|
|
102
112
|
}
|
|
103
113
|
if (args.action === "list") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sapiens-mcp",
|
|
3
|
-
"version": "1.23.
|
|
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": {
|