sapiens-mcp 1.17.0 → 1.19.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 +4 -4
- package/dist/tools/character.js +1 -1
- package/dist/tools/repertorio.js +45 -1
- package/dist/tools/video.js +78 -20
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -41,7 +41,7 @@ const TOOLS = {
|
|
|
41
41
|
handler: meta,
|
|
42
42
|
},
|
|
43
43
|
sapiens_repertorio: {
|
|
44
|
-
description: "Acervo pessoal de filme/série/anime/jogo/livro/música (Repertório, o segundo cérebro do user). Reads: list (filtros mediaType/status), search (texto em title/genres/tags), get (detalhe), lists (listas curadas), popArticles, resolve (busca capa/ano/id nos providers server-side: OMDb/IGDB-Twitch/AniList/Google Books/iTunes). Mutations (qualquer logado, mexem no PRÓPRIO acervo): add_item, update_item (status/rating/tags/note/isPublic), remove_item. CAPTURA ONE-SHOT travada na lista de providers: quando o user fala natural ('acabei de ver Duna 2, nota 9', 'tô jogando Hollow Knight', 'li tal livro'), (1) infira mediaType e status (assisti/zerei/li=completed, quero=backlog, tô jogando/vendo=active, dropei=dropped) e rating se citado; (2) chame action=resolve {mediaType, query}, escolha o candidato certo e faça add_item passando SÓ o source + externalId DELE + os campos pessoais (status/rating/tags/note). O servidor re-resolve no provider e grava título/capa/ano canônicos — você NÃO manda título/capa nem inventa externalId. (3) Se o resolve não achar (lista vazia/providerKeyMissing), NÃO dá pra adicionar: diga ao user que não encontrou nos providers (não fabrique entry manual). Upsert/dedup por (userId, source, externalId). Só pergunte se ambíguo entre candidatos.",
|
|
44
|
+
description: "Acervo pessoal de filme/série/anime/jogo/livro/música (Repertório, o segundo cérebro do user). Reads: list (filtros mediaType/status), search (texto em title/genres/tags), get (detalhe), lists (listas curadas), popArticles, resolve (busca capa/ano/id nos providers server-side: OMDb/IGDB-Twitch/AniList/Google Books/iTunes). Mutations (qualquer logado, mexem no PRÓPRIO acervo): add_item, update_item (status/rating/tags/note/isPublic), remove_item. CAPTURA ONE-SHOT travada na lista de providers: quando o user fala natural ('acabei de ver Duna 2, nota 9', 'tô jogando Hollow Knight', 'li tal livro'), (1) infira mediaType e status (assisti/zerei/li=completed, quero=backlog, tô jogando/vendo=active, dropei=dropped) e rating se citado; (2) chame action=resolve {mediaType, query}, escolha o candidato certo e faça add_item passando SÓ o source + externalId DELE + os campos pessoais (status/rating/tags/note). O servidor re-resolve no provider e grava título/capa/ano canônicos — você NÃO manda título/capa nem inventa externalId. (3) Se o resolve não achar (lista vazia/providerKeyMissing), NÃO dá pra adicionar: diga ao user que não encontrou nos providers (não fabrique entry manual). Upsert/dedup por (userId, source, externalId). Só pergunte se ambíguo entre candidatos. FERRAMENTAS DE IA (Repertório de Ferramentas): fluxo separado (vêm do catálogo aitag, não dos providers de mídia). action=search_tools {query} acha a ferramenta no catálogo e devolve o toolId; action=add_tool {toolId, favorite?, rating?, note?} grava como mediaType 'tool' (estar no acervo já é 'usei'; favorite=true liga a estrela). Use quando o user fala 'adiciona o Midjourney/Cursor no meu repertório de ferramentas' ou 'uso tal ferramenta de IA'. Cada ferramenta aponta pra página dela no aitag.",
|
|
45
45
|
schema: repertorioSchema,
|
|
46
46
|
handler: repertorio,
|
|
47
47
|
},
|
|
@@ -101,7 +101,7 @@ const TOOLS = {
|
|
|
101
101
|
handler: shorts,
|
|
102
102
|
},
|
|
103
103
|
sapiens_video: {
|
|
104
|
-
description: "Sapiens Video —
|
|
104
|
+
description: "Sapiens Video — gera vídeo (admin-only). 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, references base64 (role 'start'=imagem i2v, 'end'=frame final, 'driving'=vídeo de movimento do Motion). Custo server-side por config. Sub-action 'generate' (legado): renderiza um imageId de vídeo já criado no site. Retorna {success, url, imageId, cost}.",
|
|
105
105
|
schema: videoSchema,
|
|
106
106
|
handler: video,
|
|
107
107
|
},
|
|
@@ -126,7 +126,7 @@ const TOOLS = {
|
|
|
126
126
|
handler: profile,
|
|
127
127
|
},
|
|
128
128
|
};
|
|
129
|
-
const server = new Server({ name: "mcp-sapiens", version: "1.
|
|
129
|
+
const server = new Server({ name: "mcp-sapiens", version: "1.19.0" }, { capabilities: { tools: {} } });
|
|
130
130
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
131
131
|
tools: Object.entries(TOOLS).map(([name, t]) => ({
|
|
132
132
|
name,
|
|
@@ -159,4 +159,4 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
159
159
|
});
|
|
160
160
|
const transport = new StdioServerTransport();
|
|
161
161
|
await server.connect(transport);
|
|
162
|
-
console.error("mcp-sapiens v1.
|
|
162
|
+
console.error("mcp-sapiens v1.19.0 rodando via stdio (20 tools)");
|
package/dist/tools/character.js
CHANGED
|
@@ -43,7 +43,7 @@ export const characterSchema = z.object({
|
|
|
43
43
|
.number()
|
|
44
44
|
.int()
|
|
45
45
|
.optional()
|
|
46
|
-
.describe("Pra list_public: quantos retornar (default
|
|
46
|
+
.describe("Pra list_public: quantos retornar (default 100, max 200)."),
|
|
47
47
|
name: z
|
|
48
48
|
.string()
|
|
49
49
|
.optional()
|
package/dist/tools/repertorio.js
CHANGED
|
@@ -21,7 +21,7 @@ import { convexAction, convexMutation, convexQuery, getSessionToken, } from "../
|
|
|
21
21
|
*
|
|
22
22
|
* Action-based design (memoria: consolidar tools via args).
|
|
23
23
|
*/
|
|
24
|
-
const mediaTypeEnum = z.enum(["movie", "series", "anime", "game", "book", "music"]);
|
|
24
|
+
const mediaTypeEnum = z.enum(["movie", "series", "anime", "game", "book", "music", "tool"]);
|
|
25
25
|
const statusEnum = z.enum([
|
|
26
26
|
"backlog",
|
|
27
27
|
"active",
|
|
@@ -41,6 +41,8 @@ export const repertorioSchema = z.object({
|
|
|
41
41
|
"add_item",
|
|
42
42
|
"update_item",
|
|
43
43
|
"remove_item",
|
|
44
|
+
"search_tools",
|
|
45
|
+
"add_tool",
|
|
44
46
|
]),
|
|
45
47
|
// Reads
|
|
46
48
|
userId: z
|
|
@@ -79,6 +81,15 @@ export const repertorioSchema = z.object({
|
|
|
79
81
|
.describe("Autor(es) de livro ou artista(s) de álbum (source=itunes)."),
|
|
80
82
|
pageCount: z.number().optional(),
|
|
81
83
|
isbn: z.string().optional(),
|
|
84
|
+
// Ferramenta de IA (Repertório de Ferramentas — catálogo aitag)
|
|
85
|
+
toolId: z
|
|
86
|
+
.string()
|
|
87
|
+
.optional()
|
|
88
|
+
.describe("Pra add_tool: o toolId EXATO de um candidato do action=search_tools (catálogo aitag). Dedup por (userId, source aitag, externalId=toolId)."),
|
|
89
|
+
favorite: z
|
|
90
|
+
.boolean()
|
|
91
|
+
.optional()
|
|
92
|
+
.describe("Pra add_tool: marca a ferramenta como favorita (estrela). Estar no acervo já é 'usei'; favorita é o eixo separado de 'curto/indico'."),
|
|
82
93
|
rating: z
|
|
83
94
|
.number()
|
|
84
95
|
.nullable()
|
|
@@ -115,6 +126,24 @@ export async function repertorio(args) {
|
|
|
115
126
|
})),
|
|
116
127
|
};
|
|
117
128
|
}
|
|
129
|
+
// search_tools: busca no catálogo de ferramentas do aitag (aitagTools).
|
|
130
|
+
// Read público (catálogo), sem sessão. Devolve toolId pra usar no add_tool.
|
|
131
|
+
if (args.action === "search_tools") {
|
|
132
|
+
const tools = await convexQuery("aitag/tools:searchTools", {
|
|
133
|
+
query: need(args.query, "query"),
|
|
134
|
+
});
|
|
135
|
+
return {
|
|
136
|
+
count: Array.isArray(tools) ? tools.length : 0,
|
|
137
|
+
tools: (tools || []).map((t) => ({
|
|
138
|
+
toolId: t.toolId,
|
|
139
|
+
name: t.name,
|
|
140
|
+
category: t.categoryPt || t.categoryEn || null,
|
|
141
|
+
slug: t.slug ?? null,
|
|
142
|
+
iconUrl: t.iconUrl ?? null,
|
|
143
|
+
aitagUrl: `https://www.aitag.app/tool/${t.toolId}`,
|
|
144
|
+
})),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
118
147
|
// resolve: busca metadado (capa/ano/id) nos providers server-side, com as
|
|
119
148
|
// keys que vivem no env do Convex. Faz a captura por conversa de filme/série/
|
|
120
149
|
// jogo entrar COM capa (anime/livro/música já vêm keyless). Devolve
|
|
@@ -131,9 +160,24 @@ export async function repertorio(args) {
|
|
|
131
160
|
}
|
|
132
161
|
// Mutations: sempre exigem sessionToken
|
|
133
162
|
if (args.action === "add_item" ||
|
|
163
|
+
args.action === "add_tool" ||
|
|
134
164
|
args.action === "update_item" ||
|
|
135
165
|
args.action === "remove_item") {
|
|
136
166
|
const sessionToken = getSessionToken();
|
|
167
|
+
if (args.action === "add_tool") {
|
|
168
|
+
// Ferramenta de IA: resolve no catálogo aitag por toolId (achado via
|
|
169
|
+
// action=search_tools) e grava como mediaType "tool". Estar no acervo =
|
|
170
|
+
// usei (status completed); favorite liga a estrela.
|
|
171
|
+
return await convexAction("repertorioResolve:addToolItem", {
|
|
172
|
+
sessionToken,
|
|
173
|
+
toolId: need(args.toolId, "toolId"),
|
|
174
|
+
favorite: args.favorite,
|
|
175
|
+
rating: args.rating === null ? undefined : args.rating,
|
|
176
|
+
tags: args.tags,
|
|
177
|
+
note: args.note,
|
|
178
|
+
isPublic: args.isPublic,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
137
181
|
if (args.action === "add_item") {
|
|
138
182
|
// Travado na lista de providers: manda só o identificador (source +
|
|
139
183
|
// externalId de um resolve) + campos pessoais. O servidor re-resolve no
|
package/dist/tools/video.js
CHANGED
|
@@ -1,49 +1,107 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { convexAction, getSessionToken } from "../convexClient.js";
|
|
3
3
|
/**
|
|
4
|
-
* Sapiens Video —
|
|
4
|
+
* Sapiens Video — gera vídeo (admin-only).
|
|
5
5
|
*
|
|
6
|
-
* Sub-
|
|
7
|
-
* -
|
|
6
|
+
* Sub-actions:
|
|
7
|
+
* - create: escolhe modelo + config e gera num call só (cria a row + renderiza).
|
|
8
|
+
* Habilita Seedance/Kling/WAN/Motion (WaveSpeed) e os Veo, sem o site.
|
|
9
|
+
* - generate: renderiza um imageId de vídeo já criado no site (legado).
|
|
8
10
|
*
|
|
9
|
-
*
|
|
10
|
-
* -
|
|
11
|
-
* -
|
|
12
|
-
* -
|
|
11
|
+
* Modelos (action=create):
|
|
12
|
+
* - sapiens-video-seedance Seedance 2.0 — cena com áudio nativo, 4-15s, 480/720/1080p (t2v/i2v)
|
|
13
|
+
* - sapiens-video-kling Kling 3.0 Pro — dá vida a uma imagem, 3-15s, sound opcional (i2v/t2v)
|
|
14
|
+
* - sapiens-video-wan WAN 2.5 — imagem que fala/canta (áudio+lip-sync nativo), 5/10s (i2v)
|
|
15
|
+
* - sapiens-video-kling-motion Kling Motion — transfere o movimento de um vídeo pra uma imagem
|
|
16
|
+
* (PRECISA de pessoa com tronco visível na imagem E no vídeo)
|
|
17
|
+
* - sapiens-video-lite/fast/quality Veo 3.1 (2000/5000/25000 sinapses)
|
|
13
18
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* - sapiens-video-quality (25000 sinapses, top quality VEO 3.1)
|
|
19
|
+
* Custo: server-side por config (duração x resolução [x áudio]). i2v/motion usam
|
|
20
|
+
* `references` em base64 (role 'start' = imagem inicial, 'end' = frame final,
|
|
21
|
+
* 'driving' = vídeo de movimento do Motion).
|
|
18
22
|
*
|
|
19
|
-
* Retorna `{ success
|
|
23
|
+
* Retorna `{ success, url, imageId, cost }` ou `{ success: false, error }`.
|
|
20
24
|
*/
|
|
25
|
+
const VIDEO_MODELS = [
|
|
26
|
+
"sapiens-video-seedance",
|
|
27
|
+
"sapiens-video-kling",
|
|
28
|
+
"sapiens-video-wan",
|
|
29
|
+
"sapiens-video-kling-motion",
|
|
30
|
+
"sapiens-video-lite",
|
|
31
|
+
"sapiens-video-fast",
|
|
32
|
+
"sapiens-video-quality",
|
|
33
|
+
];
|
|
21
34
|
export const videoSchema = z.object({
|
|
22
|
-
action: z.enum(["generate"]),
|
|
35
|
+
action: z.enum(["create", "generate"]),
|
|
36
|
+
// --- action=create ---
|
|
37
|
+
model: z
|
|
38
|
+
.enum(VIDEO_MODELS)
|
|
39
|
+
.optional()
|
|
40
|
+
.describe("action=create: modelo de vídeo. 'sapiens-video-seedance' (cinematográfico+áudio, t2v/i2v), " +
|
|
41
|
+
"'sapiens-video-kling' (anima imagem, i2v/t2v), 'sapiens-video-wan' (imagem que fala, i2v), " +
|
|
42
|
+
"'sapiens-video-kling-motion' (motion transfer, precisa pessoa na imagem E no vídeo de movimento), " +
|
|
43
|
+
"'sapiens-video-lite/fast/quality' (Veo 3.1)."),
|
|
44
|
+
durationSec: z
|
|
45
|
+
.number()
|
|
46
|
+
.optional()
|
|
47
|
+
.describe("action=create (modelos WaveSpeed): duração em segundos. Seedance 4-15, Kling 3-15, WAN 5/10. " +
|
|
48
|
+
"Sem isso usa a config mais barata. O preço escala com a duração."),
|
|
49
|
+
resolution: z
|
|
50
|
+
.enum(["480p", "720p", "1080p"])
|
|
51
|
+
.optional()
|
|
52
|
+
.describe("action=create: resolução (Seedance/WAN). Default 720p. Kling não usa (1080p nativo)."),
|
|
53
|
+
audio: z
|
|
54
|
+
.boolean()
|
|
55
|
+
.optional()
|
|
56
|
+
.describe("action=create: liga áudio. Seedance = on por default; Kling 'sound' = +50%. WAN é áudio nativo sempre."),
|
|
57
|
+
// --- action=generate (legado) ---
|
|
23
58
|
imageId: z
|
|
24
59
|
.string()
|
|
25
|
-
.
|
|
26
|
-
"
|
|
60
|
+
.optional()
|
|
61
|
+
.describe("action=generate: generatedImages:_id de vídeo já criado no site. O row define modelo + custo. " +
|
|
62
|
+
"Use sapiens_gallery action=list pra descobrir."),
|
|
63
|
+
// --- comum ---
|
|
27
64
|
prompt: z
|
|
28
65
|
.string()
|
|
29
66
|
.optional()
|
|
30
|
-
.describe("Prompt
|
|
67
|
+
.describe("Prompt da cena. Pra i2v descreve o movimento. Default vago se omitido."),
|
|
31
68
|
aspectRatio: z
|
|
32
69
|
.string()
|
|
33
70
|
.optional()
|
|
34
|
-
.describe("
|
|
71
|
+
.describe("'16:9' (horizontal), '9:16' (vertical), '1:1'. Vale pro t2v; i2v herda da imagem."),
|
|
35
72
|
references: z
|
|
36
73
|
.array(z.object({
|
|
37
74
|
mimeType: z.string(),
|
|
38
75
|
data: z.string(),
|
|
39
|
-
role: z
|
|
76
|
+
role: z
|
|
77
|
+
.string()
|
|
78
|
+
.optional()
|
|
79
|
+
.describe("'start' = imagem inicial (i2v), 'end' = frame final, 'driving' = vídeo de movimento (Motion)"),
|
|
40
80
|
}))
|
|
41
81
|
.optional()
|
|
42
|
-
.describe("References
|
|
82
|
+
.describe("References em base64. i2v: role 'start' (imagem). Motion: 'start' (pessoa) + 'driving' (vídeo de movimento, <=5MB)."),
|
|
43
83
|
});
|
|
44
84
|
export async function video(args) {
|
|
85
|
+
const sessionToken = getSessionToken();
|
|
86
|
+
if (args.action === "create") {
|
|
87
|
+
if (!args.model) {
|
|
88
|
+
throw new Error("action=create exige model (ex: sapiens-video-seedance, sapiens-video-kling, sapiens-video-wan, sapiens-video-kling-motion).");
|
|
89
|
+
}
|
|
90
|
+
return await convexAction("mcpExtrasActions:mcpVideoCreateAndRender", {
|
|
91
|
+
sessionToken,
|
|
92
|
+
model: args.model,
|
|
93
|
+
prompt: args.prompt,
|
|
94
|
+
aspectRatio: args.aspectRatio,
|
|
95
|
+
durationSec: args.durationSec,
|
|
96
|
+
resolution: args.resolution,
|
|
97
|
+
audio: args.audio,
|
|
98
|
+
references: args.references,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
45
101
|
if (args.action === "generate") {
|
|
46
|
-
|
|
102
|
+
if (!args.imageId) {
|
|
103
|
+
throw new Error("action=generate exige imageId (vídeo já criado no site).");
|
|
104
|
+
}
|
|
47
105
|
return await convexAction("mcpExtrasActions:mcpVideoGenerate", {
|
|
48
106
|
sessionToken,
|
|
49
107
|
imageId: args.imageId,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sapiens-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.19.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": {
|