sapiens-mcp 1.46.0 → 1.46.2

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
@@ -53,6 +53,14 @@ O Claude avisa o custo antes de gastar, e geração que falha é estornada. Publ
53
53
 
54
54
  O servidor só conversa com o backend público do Sapiens (Convex). Sua identidade vem sempre do token de login, nunca de parâmetros soltos. Cada conta só mexe no que é dela.
55
55
 
56
+ ## Sobre o Sapiens Sintéticos
57
+
58
+ O Sapiens Sintéticos é um Studio de IA que constrói Studios: ensino modelo de pensamento na Era Sintética pra cada pessoa montar o próprio sistema de criação, com voz e régua próprias. O método se chama Borderless: 5 pilares, 7 deltas e uma sequência de 7 fases pra tirar ideia do papel (Tesão, Cruzamento, Confronto Crítico, Prototipagem, MVP, Validação, Continuidade).
59
+
60
+ Em inglês o projeto assina **Synthetic Sapiens**: an AI studio that builds studios. The missing skill is not using AI, it is inventing the system the AI feeds. That system is a studio of your own, the personal computer of 2026.
61
+
62
+ Site oficial: [sapiensinteticos.com](https://www.sapiensinteticos.com) · Manifesto: [/manifesto](https://www.sapiensinteticos.com/manifesto) · Contexto pra máquina: [/llms.txt](https://www.sapiensinteticos.com/llms.txt)
63
+
56
64
  ---
57
65
 
58
66
  Feito por [BorderLess](https://sapiensinteticos.com). Licença MIT.
package/dist/registry.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { ZodError } from "zod";
1
2
  import { zodToJsonSchema } from "zod-to-json-schema";
2
3
  import { pipeline, pipelineSchema } from "./tools/pipeline.js";
3
4
  import { image, imageSchema } from "./tools/image.js";
@@ -58,7 +59,7 @@ export const TOOLS = {
58
59
  handler: meta,
59
60
  },
60
61
  sapiens_skill: {
61
- description: "As SKILLS da casa: o passo a passo travado de cada fluxo do Sapiens, servido pelo próprio connector (sem login, sem custo, sem rede). LEIA A SKILL ANTES DE OPERAR O FLUXO — é mais barato que errar uma geração que cobra Sinapses. Sub-actions: 'list' (índice: slug + quando usar cada uma), 'get' (o SKILL.md inteiro, passe name=<slug>). Slugs: primeiros-passos (porta de entrada e as 4 armadilhas: timeout que já cobrou, disjuntor anti-loop, saldo, sessão), voz-da-casa (DNA editorial, leia antes de redigir qualquer texto publicável), musica (Musicator em 4 passos + efeito sonoro), video (qual modelo, iterar barato no Mini, storyboard por referência, sonorize), imagem (prompt full-bleed na régua da casa, multi-referência, templates), studio (o perfil de empresa da casa, o time, e gerar na identidade da pessoa vs base), tirinha (2 fases, a imagem sai SEM texto), forum-comunidade (mídia estruturada, menção, tese), companhia (o Sintético veste você), trilhas (missão e prova). O mesmo conteúdo também sai como resource MCP em skill://sapiens/<slug>/SKILL.md pra quem lê resources.",
62
+ description: "As SKILLS da casa: o passo a passo travado de cada fluxo do Sapiens, servido pelo próprio connector (sem login, sem custo, sem rede). LEIA A SKILL ANTES DE OPERAR O FLUXO — é mais barato que errar uma geração que cobra Sinapses. Sub-actions: 'list' (índice: slug + quando usar cada uma), 'get' (o SKILL.md inteiro, passe name=<slug>). Slugs: primeiros-passos (porta de entrada e as 4 armadilhas: timeout que já cobrou, disjuntor anti-loop, saldo, sessão), voz-da-casa (DNA editorial, leia antes de redigir qualquer texto publicável), musica (Musicator em 4 passos + efeito sonoro), video (qual modelo, iterar barato no Mini, storyboard por referência, sonorize), imagem (prompt full-bleed na régua da casa, multi-referência, templates), studio (fundar a casa, o perfil de empresa, o time, e por que o studio não entra na geração), tirinha (2 fases, a imagem sai SEM texto), forum-comunidade (mídia estruturada, menção, tese), companhia (o Sintético veste você), trilhas (missão e prova), minha-soul (instala o retrato do usuário como skill do projeto onde vocês estão). O mesmo conteúdo também sai como resource MCP em skill://sapiens/<slug>/SKILL.md pra quem lê resources.",
62
63
  schema: skillSchema,
63
64
  handler: skill,
64
65
  },
@@ -68,7 +69,7 @@ export const TOOLS = {
68
69
  handler: repertorio,
69
70
  },
70
71
  sapiens_gallery: {
71
- description: "Browse, publicação e upload das imagens do user. Sub-actions: list (últimas N imagens, com prompt/model/url + isPublic), get (1 imagem com metadados, opcionalmente base64), publish (torna a PRÓPRIA imagem pública: entra na galeria pública + feed Pinterest, e ganha página indexável /imagem/<id> se o modelo não for degen — devolve publicPageUrl), unpublish (volta a privada), upload (traz pra galeria uma peça gerada FORA da casa, por sourceUrl https, filePath local ou base64; ADMIN por ora). Use list/get pra reusar imagem como referência (passe o imageId em sapiens_image mode=edit ou mode=variation) ou pra mostrar pro user o que ele já tem; publish quando o user quer divulgar a imagem dele. IMPORTANTE sobre upload: a peça entra PRIVADA e continua privada — o servidor recusa publicar e recusa compartilhar na comunidade, porque ela é do usuário e a responsabilidade é dele. O caminho pra ela virar coisa pública é usar como referência numa geração daqui e publicar o resultado.",
72
+ description: "Browse, publicação e upload das imagens do user. Sub-actions: list (últimas N imagens, com prompt/model/url + isPublic), get (1 imagem com metadados, opcionalmente base64), publish (torna a PRÓPRIA imagem pública: entra na galeria pública + feed Pinterest, e ganha página indexável /imagem/<id> se o modelo não for degen — devolve publicPageUrl), unpublish (volta a privada), upload (traz pra galeria uma peça gerada FORA da casa, por sourceUrl https, filePath local ou base64; ADMIN por ora), refs (acopla peças da casa como REFERÊNCIA numa peça que já existe, de upload ou de ingest; ADMIN, e só na própria peça). Use list/get pra reusar imagem como referência (passe o imageId em sapiens_image mode=edit ou mode=variation) ou pra mostrar pro user o que ele já tem; publish quando o user quer divulgar a imagem dele. IMPORTANTE sobre upload: a peça entra PRIVADA e continua privada — o servidor recusa publicar e recusa compartilhar na comunidade, porque ela é do usuário e a responsabilidade é dele. O caminho pra ela virar coisa pública é usar como referência numa geração daqui e publicar o resultado. O refs escreve só proveniência (de que ficha/folha a peça nasceu): não promove nada, e um upload continua sem publicar depois de ganhar referência.",
72
73
  schema: gallerySchema,
73
74
  handler: gallery,
74
75
  },
@@ -98,7 +99,7 @@ export const TOOLS = {
98
99
  handler: search,
99
100
  },
100
101
  sapiens_studios: {
101
- description: "Catálogo dos estúdios/experimentos Sapiens + o SEU studio + a Emancipação. Sub-actions: mine (o studio do user, que é o perfil de EMPRESA da casa dele: nome/endereço público/marca/operador/tamanho do time), 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.",
102
+ description: "O SEU studio (o perfil de EMPRESA da casa do user) + o catálogo dos estúdios/experimentos Sapiens. Sub-actions: mine (o studio do user: nome/endereço público/marca/operador/tamanho do time), create (FUNDA um studio novo, exige name; nasce privado, de graça, teto de 4 por pessoa), list (catálogo com URL+status+tags+mcpReady), get (detalhe de 1 slug), publishable_url (formata URL /articles/<slug>). Use quando user pergunta 'que estúdios existem', 'qual o meu studio', ou diz 'quero abrir/fundar meu studio', 'criar a página da minha empresa'. Estúdios cobertos: helen-voice, musicator, persona-sapiens, personagem-atlas, sapiens-shorts, sapiens-video, text-post-builder, comic-builder, carrosel-editorial, comunidade, repertorio.",
102
103
  schema: studiosSchema,
103
104
  handler: studios,
104
105
  },
@@ -148,7 +149,7 @@ export const TOOLS = {
148
149
  handler: character,
149
150
  },
150
151
  sapiens_profile: {
151
- description: "O 'tudo junto' do perfil do user (/u/<username>), user-tier. Agrega o que mora no perfil mas estava fora do MCP: identidade + nível/XP + saldo, badges (conquistas) e golden tools (favoritos do aitag). Sub-actions de LEITURA: get (card completo: identidade + nível + saldo + badges + golden tools), badges (só as conquistas, lista cheia), golden_tools (só os favoritos do aitag, lista cheia), notifications (suas notificações recentes do sino + contagem de não-lidas), mark_read (marca uma notificationId ou TODAS as não-lidas como lidas). Sub-actions de ESCRITA (mexem na SUA conta; identidade sempre da sessão): follow/unfollow (seguir/deixar de seguir outro user por followingId=users:_id, descoberto via sapiens_community participants/search_users), update_bio (edita a sua bio), update_username (troca o seu @; inválido/tomado volta {success:false,error}). FAVORITOS de ferramentas de IA (Golden Tools do aitag; o toolId vem de sapiens_repertorio action=search_tools): favorite_tool (favorita/desfavorita, estrela), favorite_lists (suas listas), create_favorite_list (listName+emoji/description/isPublic), add_to_favorite_list/remove_from_favorite_list (toolId+listId), delete_favorite_list (listId). As partes grandes do perfil NÃO são duplicadas aqui, têm tool própria: imagens geradas/publicadas=sapiens_gallery, repertório (filmes/séries/jogos/livros/música)=sapiens_repertorio, personagens=sapiens_character, persona/arquétipo MBTI=sapiens_persona action=my_profile, saldo detalhado por bucket=sapiens_meta action=subscription. Monta a partir de queries já em prod (sem custo).",
152
+ description: "O 'tudo junto' do perfil do user (/u/<username>), user-tier. Agrega o que mora no perfil mas estava fora do MCP: identidade + nível/XP + saldo, badges (conquistas) e golden tools (favoritos do aitag). Sub-actions de LEITURA: get (card completo: identidade + nível + saldo + badges + golden tools), soul (a sapiens-soul: retrato do momento no contrato sapiens.soul/v1, identidade + persona + repertório público + tese, derivado e datado; quando o user pedir 'minha soul' ou 'meu retrato do Sapiens' pro projeto dele, chame e salve como sapiens-soul.json — snapshot, não conexão viva), badges (só as conquistas, lista cheia), golden_tools (só os favoritos do aitag, lista cheia), notifications (suas notificações recentes do sino + contagem de não-lidas), mark_read (marca uma notificationId ou TODAS as não-lidas como lidas). Sub-actions de ESCRITA (mexem na SUA conta; identidade sempre da sessão): follow/unfollow (seguir/deixar de seguir outro user por followingId=users:_id, descoberto via sapiens_community participants/search_users), update_bio (edita a sua bio), update_username (troca o seu @; inválido/tomado volta {success:false,error}). FAVORITOS de ferramentas de IA (Golden Tools do aitag; o toolId vem de sapiens_repertorio action=search_tools): favorite_tool (favorita/desfavorita, estrela), favorite_lists (suas listas), create_favorite_list (listName+emoji/description/isPublic), add_to_favorite_list/remove_from_favorite_list (toolId+listId), delete_favorite_list (listId). As partes grandes do perfil NÃO são duplicadas aqui, têm tool própria: imagens geradas/publicadas=sapiens_gallery, repertório (filmes/séries/jogos/livros/música)=sapiens_repertorio, personagens=sapiens_character, persona/arquétipo MBTI=sapiens_persona action=my_profile, saldo detalhado por bucket=sapiens_meta action=subscription. Monta a partir de queries já em prod (sem custo).",
152
153
  schema: profileSchema,
153
154
  handler: profile,
154
155
  },
@@ -226,9 +227,8 @@ Cliente que lê resources MCP acha o MESMO conteúdo em skill://sapiens/<slug>/S
226
227
  REGRA DE OURO:
227
228
  - PRIMEIRO CONTATO ou "o que você faz?"/"como começo?"/"o que dá pra fazer?": chame sapiens_meta action=start e MOSTRE o resultado na sua voz. Sem login, ele ensina a conectar; logado, traz saldo + primeiros poderes com exemplos. É a porta de entrada: não despeje a lista inteira de tools, deixe o start guiar.
228
229
  - LOGO APÓS UM LOGIN BEM-SUCEDIDO (action=login retornou ok): chame action=start na sequência e mostre a porta de entrada. O recém-chegado não sabe o que pedir; não o deixe na tela em branco, guie a primeira jogada sem ele precisar perguntar.
229
- - Se um tool voltar erro de validação ("exige X", "falta Y"), LEIA o erro e refaça a chamada COM o que falta. NUNCA repita igual a chamada que falhou: 3 falhas seguidas no mesmo tool fazem o cliente marcar o servidor como "unreachable" por ~56s (disjuntor anti-loop). Aí parece que "o MCP caiu", quando foi só argumento faltando.
230
230
  - Antes de gerar algo caro (imagem/música/vídeo), cheque saldo: sapiens_meta action=credits (ou subscription). Saldo baixo, avise o usuário antes. Vídeo é o mais caro da casa: confirme com ele antes de disparar.
231
- - REGRA DO TIMEOUT (vale pra toda geração SÍNCRONA: imagem pesada, artigo, mega-gráfico, carrossel): a chamada pode estourar o teto de ~120s do cliente e voltar 'Timeout' MESMO tendo gerado e COBRADO. Nunca repita às cegas: confira antes onde o resultado cairia (imagem: sapiens_gallery action=list; artigo: sapiens_write action=list; carrossel: sapiens_pipeline action=list_carousels; pipeline: /dashboard/admin/content).
231
+ - REGRA DO TIMEOUT: geração SÍNCRONA pode estourar o teto de ~120s do cliente e voltar 'Timeout' MESMO tendo gerado e COBRADO. Nunca repita às cegas: confira antes onde o resultado cairia (a tabela de onde conferir está na skill 'primeiros-passos').
232
232
  - "sessionToken expirado" = refaça login: sapiens_meta action=login com o código de sapiensinteticos.com/conectar-claude.
233
233
  - sapiens_meta action=formats devolve os schemas por formato; action=whoami diz tier (user/admin) + saldo. sapiens_image e sapiens_video action=models trazem o catálogo com o preço ATUAL: consulte em vez de chutar custo.
234
234
 
@@ -265,7 +265,7 @@ const TOOL_TITLES = {
265
265
  sapiens_write: "Artigos do Perfil",
266
266
  sapiens_quote_pop: "Coluna Sapiens/Repertório",
267
267
  sapiens_search: "Buscar Artigos",
268
- sapiens_studios: "Estúdios & Emancipação",
268
+ sapiens_studios: "Meu Studio & Estúdios",
269
269
  sapiens_persona: "Persona (MBTI)",
270
270
  sapiens_helen: "Voz Helen (TTS)",
271
271
  sapiens_musicator: "Musicator",
@@ -353,8 +353,23 @@ export async function callTool(name, rawArgs) {
353
353
  }
354
354
  catch (e) {
355
355
  return {
356
- content: [{ type: "text", text: `Erro: ${describeConvexError(e)}` }],
356
+ content: [
357
+ { type: "text", text: `Erro: ${describeConvexError(e)}${retryHint(e)}` },
358
+ ],
357
359
  isError: true,
358
360
  };
359
361
  }
360
362
  }
363
+ /**
364
+ * A regra do disjuntor viaja NO ERRO, não no handshake. Ela só passa a valer
365
+ * depois que uma chamada falhou, então cobrar ~300 chars de TODA conversa pra
366
+ * avisar de algo que talvez nunca aconteça é caro: aqui ela chega no instante
367
+ * exato em que aplica e custa zero nas outras. Só em erro de validação (Zod):
368
+ * erro de saldo ou de sessão tem o próprio conserto, e repetir a chamada
369
+ * idêntica não é o risco deles.
370
+ */
371
+ function retryHint(e) {
372
+ if (!(e instanceof ZodError))
373
+ return "";
374
+ return " -> Refaça a chamada COM o campo corrigido. NUNCA repita idêntica: 3 falhas seguidas no mesmo tool fazem o cliente marcar o servidor como 'unreachable' por ~56s, e aí parece que o MCP caiu quando foi só argumento faltando.";
375
+ }
package/dist/skills.js CHANGED
@@ -262,15 +262,24 @@ Aí não é geração base, é o Studio dela. Veja a skill \`studio\`.`,
262
262
  },
263
263
  {
264
264
  name: "studio",
265
- title: "Meu Studio e a Emancipação",
266
- description: "O studio é a PÁGINA de portfólio da pessoa (obra, time, endereço público). Puxe quando ela falar do studio dela, quiser mostrar a casa, montar time, ou pedir casa própria.",
265
+ title: "Meu Studio",
266
+ description: "O studio é a PÁGINA de portfólio da empresa da pessoa (obra, time, endereço público), e você funda o dela pelo chat. Puxe quando ela falar do studio dela, quiser fundar um, mostrar a casa ou montar time.",
267
267
  body: `## O que o studio é
268
268
 
269
- O studio é a **página de portfólio** da pessoa: um endereço público (\`sapiensinteticos.com/studios/<slug>\`) com a obra, o time e o que a casa faz. Não é uma paleta de ferramentas, não tem níveis, e **não é identidade de geração**: isso morreu.
269
+ O studio é a **página de portfólio** da empresa da pessoa: um endereço público (\`sapiensinteticos.com/studios/<slug>\`) com a obra, o time e o que a casa faz. Não é uma paleta de ferramentas, não tem níveis, e **não é identidade de geração**: isso morreu.
270
270
 
271
271
  Uma pessoa pode ter até 4 studios e integrar até 4. \`sapiens_studios action=mine\` mostra o primário dela: nome, endereço público, marca e tamanho do time. O servidor resolve pela SESSÃO, você nunca passa id.
272
272
 
273
- Quem edita a página (obra, time, sobre, capa, logo, folha) é o membro, na própria página, no modo edição. Você não edita isso pelo MCP.
273
+ ## Fundar
274
+
275
+ \`sapiens_studios action=create name="<nome da casa>"\` funda. De graça, sem Sinapse.
276
+
277
+ - **Pergunte o nome, nunca invente.** O endereço público sai dele, em kebab-case, e é o que vai no cartão de visita da pessoa.
278
+ - \`description\` (uma linha) é opcional e aparece embaixo do nome. \`brandSlug\` ancora a marca dela, se ela já tiver uma.
279
+ - Nasce **privado**, com ela como fundadora. Publicar é decisão dela, na página.
280
+ - Bateu o teto de 4: o erro diz. Não insista, ela sai de um pra fundar outro.
281
+
282
+ Depois de fundar, entregue o link e pare. Vestir a página (capa, logo, sobre, seções, ordem da obra) é no modo edição da própria página, não por aqui.
274
283
 
275
284
  ## O studio NÃO entra na geração
276
285
 
@@ -286,15 +295,7 @@ Pergunte qual, não adivinhe.
286
295
 
287
296
  Gente entra por convite, e o convite exige **follow mútuo**: os dois precisam já se seguir. Personagem do próprio elenco entra sem convite, é posse. Teto de 4 pessoas por casa.
288
297
 
289
- A obra da página é automática por default: a soma dos Destaques de quem está no time. Se alguém empurrar uma peça à mão, ela passa a ser curada. Tudo isso se faz na página, não por aqui.
290
-
291
- ## Emancipação
292
-
293
- O passo grande: quando o membro quer a casa PRÓPRIA dele, site ou produto próprio, FORA do Sapiens.
294
-
295
- \`sapiens_studios action=emancipar\` devolve o blueprint pra VOCÊ construir na infra DELE (Vercel, Convex e domínio dele), começando pela Fundação e seguindo um módulo por vez (\`action=module module=<slug>\`: fundacao, sapiens-connect, telegram, email, auth).
296
-
297
- Confirme cada passo. Nunca hospede no Sapiens. Siga o gosto dele. É complexo: conduza com calma.`,
298
+ A obra da página é automática por default: a soma dos Destaques de quem está no time. Se alguém empurrar uma peça à mão, ela passa a ser curada. Tudo isso se faz na página, não por aqui.`,
298
299
  },
299
300
  {
300
301
  name: "tirinha",
@@ -402,6 +403,65 @@ Sem bloco companion, opere na voz neutra da casa.`,
402
403
 
403
404
  **O crédito em Sinapses sai na APROVAÇÃO dele, não na hora.** Não prometa Sinapse no momento do claim. Diga que a prova foi enviada e que o crédito vem quando for aprovada.`,
404
405
  },
406
+ {
407
+ name: "minha-soul",
408
+ title: "Instalar a soul do usuário neste projeto",
409
+ description: "Transforma o retrato do usuário no Sapiens numa skill local do projeto atual, pra toda conversa nesta pasta já saber quem é o dono. Puxe quando ele disser 'instala minha soul', 'meu Claude precisa me conhecer', 'personaliza esse projeto comigo'.",
410
+ body: `## O que você vai fazer
411
+
412
+ Escrever a soul da pessoa como skill LOCAL do projeto onde vocês estão. Depois disso, toda conversa nesta pasta abre já sabendo quem é o dono: o gosto, o jeito de pensar, o repertório. Sem MCP no meio, sem chave, sem rede.
413
+
414
+ ## Passo a passo
415
+
416
+ 1. \`sapiens_profile action=soul\` devolve o contrato \`sapiens.soul/v1\`. Não cobra Sinapse.
417
+ 2. Escreva \`.claude/skills/sapiens-soul/SKILL.md\` na raiz do projeto atual, no molde abaixo. Pasta que não existe, você cria. Arquivo que já existe, você sobrescreve: é o retrato de hoje.
418
+ 3. Confirme em UMA linha: o caminho do arquivo e a data do retrato. Não despeje o JSON na conversa.
419
+
420
+ Se a pessoa não estiver dentro de um projeto (conversa sem pasta, claude.ai), diga onde o arquivo deveria morar e ofereça o conteúdo pra ela colar.
421
+
422
+ ## O molde
423
+
424
+ O corpo em prosa é o que o modelo lê rápido; o JSON no fim é pra quem quiser o dado estruturado. Preencha com o que veio da soul e corte a seção que voltou vazia.
425
+
426
+ \`\`\`\`markdown
427
+ ---
428
+ name: sapiens-soul
429
+ description: Quem é o dono deste projeto (nome, @, persona cognitiva, repertório e tese, vindos do Sapiens Sintéticos). Puxe antes de escrever copy, escolher exemplo, nomear coisa, decidir estética ou sugerir referência.
430
+ ---
431
+
432
+ # A soul de <nome ou @handle>
433
+
434
+ Retrato de <data legível do generatedAt>, contrato sapiens.soul/v1. É um instantâneo, não uma conexão viva.
435
+
436
+ ## Quem é
437
+ <subject: nome, @, bio, nível, badges, link do perfil>
438
+
439
+ ## Como pensa
440
+ <cognition: código, persona, e os quatro eixos com polo e confiança, em uma linha cada>
441
+
442
+ ## Repertório
443
+ <repertorio: total público, gêneros e tags que mais aparecem, nota média, pessoas que ele acompanha, e as obras recentes que dizem alguma coisa sobre o gosto>
444
+
445
+ ## Tese
446
+ <tese, quando veio>
447
+
448
+ ## Como usar isto
449
+ Escrevendo pra essa pessoa ou no lugar dela: puxe a referência do repertório dela antes de inventar uma. O jeito de decidir está nos eixos. Na dúvida entre duas opções, escolha a que combina com o que já está aqui.
450
+
451
+ ## Dados brutos
452
+ \`\`\`json
453
+ <o contrato inteiro, como veio>
454
+ \`\`\`
455
+ \`\`\`\`
456
+
457
+ ## Atualizar
458
+
459
+ O retrato envelhece. Quando a pessoa pedir, rode a action de novo e sobrescreva o arquivo. Nada mais precisa mudar.
460
+
461
+ ## O que nunca entra
462
+
463
+ A soul é derivada e já sai filtrada da casa: saldo, transações, e-mail, contatos, conversas e criações privadas não viajam. Não tente completar o retrato com dado de outra tool.`,
464
+ },
405
465
  ];
406
466
  export const SKILLS = Object.fromEntries(SKILL_LIST.map((s) => [s.name, s]));
407
467
  /** Monta o SKILL.md completo (frontmatter do formato aberto + corpo). */
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { readFile } from "node:fs/promises";
3
- import { convexAction, getSessionToken, isRemoteContext } from "../convexClient.js";
3
+ import { convexAction, convexMutation, getSessionToken, isRemoteContext, } from "../convexClient.js";
4
4
  /**
5
5
  * Galeria do usuário: browse, publicação e UPLOAD de peça de fora.
6
6
  * Wrapper sobre `desktopMcp.galleryList` / `galleryGet` / `gallerySetPublic` e
@@ -27,7 +27,15 @@ import { convexAction, getSessionToken, isRemoteContext } from "../convexClient.
27
27
  * e um motor terceiro só renderizou é ingest.
28
28
  */
29
29
  export const gallerySchema = z.object({
30
- action: z.enum(["list", "get", "publish", "unpublish", "upload", "ingest"]),
30
+ action: z.enum([
31
+ "list",
32
+ "get",
33
+ "publish",
34
+ "unpublish",
35
+ "upload",
36
+ "ingest",
37
+ "refs",
38
+ ]),
31
39
  limit: z
32
40
  .number()
33
41
  .int()
@@ -42,7 +50,7 @@ export const gallerySchema = z.object({
42
50
  imageId: z
43
51
  .string()
44
52
  .optional()
45
- .describe("generatedImages:_id (obrigatório pra action=get/publish/unpublish)"),
53
+ .describe("generatedImages:_id (obrigatório pra action=get/publish/unpublish/refs)"),
46
54
  includeBase64: z
47
55
  .boolean()
48
56
  .optional()
@@ -89,11 +97,18 @@ export const gallerySchema = z.object({
89
97
  .string()
90
98
  .optional()
91
99
  .describe("action=ingest: resolução ('720p', '1080p'). Default 720p."),
100
+ // Aceita lista OU string com os ids separados por vírgula. O segundo formato
101
+ // existe porque alguns clientes MCP serializam array como texto ao repassar a
102
+ // chamada, e aí o servidor recusava com "Expected array, received string" sem
103
+ // que houvesse nada de errado com o pedido. Normalizado logo abaixo.
92
104
  referenceImageIds: z
93
- .array(z.string())
94
- .max(8)
105
+ .union([z.array(z.string()), z.string()])
95
106
  .optional()
96
- .describe("action=ingest: generatedImages:_id das peças DA CASA que serviram de referência (folha de storyboard, ficha de personagem, frame inicial). Ficam acopladas à peça ingerida e aparecem na ficha dela: é o que prova que a receita saiu daqui. Só peça sua entra."),
107
+ .describe("action=ingest/refs: generatedImages:_id das peças DA CASA que serviram de referência (folha de storyboard, ficha de personagem, frame inicial). Ficam acopladas à peça e aparecem na ficha dela: é o que prova que a receita saiu daqui. Só peça sua entra, no máximo 8. Aceita lista ou os ids separados por vírgula."),
108
+ replace: z
109
+ .boolean()
110
+ .optional()
111
+ .describe("action=refs: false (default) SOMA às referências que já existem; true troca a lista inteira. Pra limpar tudo, mande a lista vazia com replace=true."),
97
112
  uploadedSource: z
98
113
  .string()
99
114
  .optional()
@@ -190,6 +205,23 @@ async function resolveUploadBytes(args) {
190
205
  }
191
206
  throw new Error("action=upload exige a peça: sourceUrl (https), filePath (local, stdio) ou base64.");
192
207
  }
208
+ /**
209
+ * Normaliza `referenceImageIds` pro formato que o Convex espera (lista).
210
+ *
211
+ * O schema aceita lista ou string com vírgulas porque cliente MCP que serializa
212
+ * array como texto existe de verdade, e o erro que ele produzia ("Expected
213
+ * array, received string") não dizia nada sobre como consertar. Aqui a diferença
214
+ * morre, e o backend continua recebendo sempre uma lista.
215
+ */
216
+ function normalizeRefIds(raw) {
217
+ if (!raw)
218
+ return [];
219
+ const lista = Array.isArray(raw) ? raw : raw.split(",");
220
+ return lista
221
+ .map((id) => id.trim().replace(/^["'[]+|["'\]]+$/g, ""))
222
+ .filter(Boolean)
223
+ .slice(0, 8);
224
+ }
193
225
  export async function gallery(args) {
194
226
  const sessionToken = getSessionToken();
195
227
  if (args.action === "list") {
@@ -257,11 +289,30 @@ export async function gallery(args) {
257
289
  externalCost: args.externalCost,
258
290
  aspectRatio: args.aspectRatio,
259
291
  size: args.size,
260
- referenceImageIds: args.referenceImageIds,
292
+ referenceImageIds: normalizeRefIds(args.referenceImageIds),
261
293
  });
262
294
  return {
263
295
  ...result,
264
296
  aviso: "Peça NATIVA: entra na timeline, publica e vai pra comunidade como qualquer geração da casa. Custo em Sinapses é 0 e ela NÃO leva assinatura C2PA, porque o motor rodou fora. Use ingest só quando a receita (folha, personagem, prompt) foi da casa; peça achada pronta é action=upload.",
265
297
  };
266
298
  }
299
+ // refs: acopla peças da casa a uma peça que JÁ existe (upload ou ingest).
300
+ // A ingestão aceita refs no nascimento, mas quem sobe raramente tem os ids em
301
+ // mãos na hora; e o upload não tinha lugar nenhum pra dizer de onde veio a
302
+ // direção. Escreve proveniência e nada mais: upload continua sem publicar.
303
+ if (args.action === "refs") {
304
+ if (!args.imageId) {
305
+ throw new Error("action=refs exige imageId: a peça que RECEBE as referências. Use action=list pra achar.");
306
+ }
307
+ const referenceImageIds = normalizeRefIds(args.referenceImageIds);
308
+ if (!referenceImageIds.length && !args.replace) {
309
+ throw new Error("action=refs exige referenceImageIds: os ids das peças da casa que serviram de referência. Pra LIMPAR as referências, mande replace=true com a lista vazia.");
310
+ }
311
+ return await convexMutation("externalIngest:mcpAttachReferences", {
312
+ sessionToken,
313
+ imageId: args.imageId,
314
+ referenceImageIds,
315
+ replace: args.replace,
316
+ });
317
+ }
267
318
  }
@@ -18,6 +18,10 @@ import { convexQuery, convexMutation, getSessionToken } from "../convexClient.js
18
18
  *
19
19
  * Sub-actions:
20
20
  * - get: card completo (identidade + nível + saldo + badges + golden tools).
21
+ * - soul: a sapiens-soul: retrato do momento (contrato sapiens.soul/v1,
22
+ * identidade + persona + repertório + tese, derivado e datado).
23
+ * Salve como sapiens-soul.json no projeto do usuário quando ele
24
+ * pedir "pega minha soul / meu retrato do Sapiens".
21
25
  * - badges: só as conquistas (lista cheia).
22
26
  * - golden_tools: só os favoritos do aitag (lista cheia).
23
27
  * - notifications: suas notificações recentes (sino) + contagem de não-lidas.
@@ -33,6 +37,7 @@ import { convexQuery, convexMutation, getSessionToken } from "../convexClient.js
33
37
  export const profileSchema = z.object({
34
38
  action: z.enum([
35
39
  "get",
40
+ "soul",
36
41
  "badges",
37
42
  "golden_tools",
38
43
  "notifications",
@@ -119,6 +124,15 @@ function mapTools(rows) {
119
124
  }
120
125
  export async function profile(args) {
121
126
  const sessionToken = getSessionToken();
127
+ // A soul: retrato do momento (contrato sapiens.soul/v1). Autentica pelo
128
+ // próprio sessionToken; token de membro tem acesso total, então vem inteira.
129
+ if (args.action === "soul") {
130
+ const soul = await convexQuery("soulContract:getForToken", { sessionToken });
131
+ return {
132
+ ...soul,
133
+ note: "Snapshot datado (generatedAt), não é conexão viva. Salve como sapiens-soul.json no projeto do usuário; pra atualizar, rode a action de novo e sobrescreva o arquivo.",
134
+ };
135
+ }
122
136
  // notifications/mark_read autenticam pelo próprio sessionToken (requireMcpUser
123
137
  // no Convex), não precisam do resolveUser — atalham antes.
124
138
  if (args.action === "notifications") {
@@ -1,31 +1,42 @@
1
1
  import { z } from "zod";
2
- import { convexAction, convexQuery, getSessionToken } from "../convexClient.js";
2
+ import { convexAction, convexMutation, getSessionToken } from "../convexClient.js";
3
3
  /**
4
- * Catálogo dos estúdios/experimentos Sapiens (v1.3).
4
+ * Duas coisas com o mesmo nome, e o `action` separa as duas:
5
5
  *
6
- * Versão minimal: retorna info estática sobre os experimentos disponíveis no
7
- * app. Cada entry tem URL do dashboard, descrição curta, status (estável /
8
- * beta / experimental), e tags. Útil pro Claude rotear o user pro lugar
9
- * certo quando ele pede coisa que não está coberta no plugin v1.x.
6
+ * 1. O CATÁLOGO dos estúdios/experimentos da casa (`list`/`get`): info estática
7
+ * com URL do dashboard, status e tags, pro Claude rotear o user pro lugar
8
+ * certo quando ele pede coisa que o plugin não cobre.
9
+ * 2. O STUDIO DO MEMBRO (`mine`/`create`): a página de portfólio da empresa
10
+ * dele. Fundar é aqui; vestir a página (capa, logo, sobre, seções) e montar
11
+ * o time é na própria página, no modo edição.
10
12
  *
11
- * v1.4 vai adicionar wrappers session-token-auth pros estúdios que justificam
12
- * (Helen Voice TTS, Musicator letras, Sapiens Shorts render, Persona Sapiens).
13
- * Por hora skills usam Claude-side + dashboard URL.
13
+ * A Emancipação (o antigo "Nível 3", blueprint pra construir a casa própria na
14
+ * infra do membro) saiu em ago/2026 junto com os níveis: o studio virou perfil
15
+ * de empresa, não uma jornada. As queries `studioBlueprints:mcp*` seguem no
16
+ * backend só como aviso pra pacote publicado que ainda chama.
14
17
  */
15
18
  export const studiosSchema = z.object({
16
19
  action: z
17
- .enum(["list", "get", "publishable_url", "mine", "emancipar", "module"])
18
- .describe("mine = o SEU studio (a página de portfólio da sua casa): nome, endereço público, marca, operador e tamanho do time. list/get = catálogo de estúdios da casa. publishable_url = URL de artigo. " +
19
- "emancipar = INICIA a Emancipação (Nível 3 do Studio): puxa o blueprint-mestre pra VOCÊ (Claude) construir a casa PRÓPRIA do membro, na infra DELE (Vercel + Convex + domínio dele), FORA do Sapiens. Devolve a identidade do studio dele hidratada + o índice de módulos + o guia da Fundação. " +
20
- "module = puxa o guia de UM módulo de infra pra continuar a construção (ex: fundacao, sapiens-connect, midia, telegram, email, auth)."),
20
+ .enum(["list", "get", "publishable_url", "mine", "create"])
21
+ .describe("mine = o SEU studio (a página de portfólio da sua casa): nome, endereço público, marca, operador e tamanho do time. " +
22
+ "create = FUNDA um studio novo na conta do user (exige 'name'). Nasce privado, com ele como fundador; publicar e vestir a página é na própria página. Teto de 4 por pessoa. " +
23
+ "list/get = catálogo de estúdios da casa. publishable_url = URL de artigo."),
21
24
  studio: z
22
25
  .string()
23
26
  .optional()
24
27
  .describe("Pra action=get: slug do estúdio (ex 'helen-voice', 'musicator', 'persona-sapiens', 'sapiens-shorts')."),
25
- module: z
28
+ name: z
26
29
  .string()
27
30
  .optional()
28
- .describe("Pra action=module: slug do módulo de infra da emancipação. Prontos (com guia): fundacao, sapiens-connect, midia, telegram, email, auth. Chegando (no índice, sem guia ainda): pagamentos, analytics."),
31
+ .describe("Pra action=create: o nome da casa (obrigatório). O endereço público sai daí, em kebab-case. Pergunte ao user, não invente."),
32
+ description: z
33
+ .string()
34
+ .optional()
35
+ .describe("Pra action=create: uma linha do que a casa faz (até 200 chars). Opcional, mas é o que aparece embaixo do nome na página."),
36
+ brandSlug: z
37
+ .string()
38
+ .optional()
39
+ .describe("Pra action=create: marca da casa (slug de sapiens_brand). Opcional. Só passa marca que é do user."),
29
40
  publishableId: z
30
41
  .string()
31
42
  .optional()
@@ -202,28 +213,24 @@ export async function studios(args) {
202
213
  : "publishableId lookup ainda não suportado v1.3. Passe slug.",
203
214
  };
204
215
  }
205
- // action=emancipar: o blueprint-mestre da Emancipação (Nível 3). O membro sai
206
- // da casa do Sapiens e monta a PRÓPRIA, na infra dele, guiado pelo Claude dele.
207
- if (args.action === "emancipar") {
208
- const sessionToken = getSessionToken();
209
- const res = await convexQuery("studioBlueprints:mcpEmanciparBlueprint", { sessionToken });
210
- if (!res?.ok)
211
- return res; // { ok:false, error } — ex: sem studio montado ainda
212
- return {
213
- ...res,
214
- howto: "Blueprint-mestre da Emancipação (Nível 3). VOCÊ (o Claude do membro) constrói a casa dele NUMA PASTA NOVA, fora deste projeto, com Vercel + Convex + domínio DO MEMBRO. O Sapiens NÃO hospeda a casa. Conduza assim: (1) leia o `blueprint` (guia da Fundação) e execute passo a passo, confirmando com o membro ANTES de criar conta ou gastar (domínio); (2) o selo <meta name=\"sapiens-studio\"> é OBRIGATÓRIO pra verificação; (3) com a casa no ar, o membro submete a URL no Sapiens (Rito 'Studio no Ar', que paga Sinapses); (4) pra cada próximo módulo do `modules` que estiver 'ready', chame sapiens_studios action=module module=<slug>. Um módulo por vez, sem atropelar. É um passo grande: vá com calma e no gosto do membro.",
215
- };
216
- }
217
- // action=module: o guia de UM módulo de infra, hidratado com a identidade do
218
- // studio do membro. Chame depois da fundacao, um por vez.
219
- if (args.action === "module") {
220
- if (!args.module) {
221
- throw new Error("action=module exige 'module' (slug do módulo de infra: fundacao, sapiens-connect, telegram, email, auth).");
216
+ // action=create: funda a casa. O servidor resolve o dono pelo sessionToken e
217
+ // devolve o endereço público montado (quem chama não tem tela pra procurar).
218
+ if (args.action === "create") {
219
+ const name = (args.name ?? "").trim();
220
+ if (!name) {
221
+ throw new Error("action=create exige 'name' (o nome da casa). Pergunte ao user antes de inventar.");
222
222
  }
223
223
  const sessionToken = getSessionToken();
224
- return await convexQuery("studioBlueprints:mcpModuleBlueprint", {
224
+ const res = await convexMutation("studios:mcpCreateStudio", {
225
225
  sessionToken,
226
- module: args.module,
226
+ name,
227
+ description: args.description,
228
+ brandSlug: args.brandSlug,
227
229
  });
230
+ return {
231
+ ...res,
232
+ note: "Studio fundado, privado por enquanto. O user publica e veste a página (capa, logo, sobre, time, obra) em " +
233
+ `${APP}/studios/${res.slug} no modo edição. Convidar gente exige follow mútuo.`,
234
+ };
228
235
  }
229
236
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sapiens-mcp",
3
- "version": "1.46.0",
3
+ "version": "1.46.2",
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": {