sapiens-mcp 1.79.0 → 1.80.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/README.md CHANGED
@@ -68,6 +68,8 @@ Tem crédito na fal ou na Kie? Ponha a chave no `env` do servidor, no config do
68
68
 
69
69
  No Claude Code: `claude mcp add sapiens --env FAL_KEY=sua-chave -- npx -y sapiens-mcp`. Depois é só pedir: "gera um retrato da Vera na fal com a minha chave".
70
70
 
71
+ No Claude Desktop tem um caminho sem config: a extensão [sapiens.mcpb](https://sapiensinteticos.b-cdn.net/mcpb/sapiens.mcpb). Abra o arquivo e o Claude pede a chave da fal e da Kie em campos próprios, que ficam guardados no cofre do sistema. Versão nova é baixar o arquivo de novo e abrir.
72
+
71
73
  A chave fica na sua máquina e sai dela só pro provedor: não vai em argumento, não volta em resultado, não passa pelo servidor do Sapiens. Por isso a `sapiens_pontes` existe só aqui, no servidor instalado; o conector remoto (claude.ai, ChatGPT) não recebe chave. Os jobs ficam em `~/.sapiens-mcp/pontes-jobs.json`, e o mesmo pedido repetido em 30 minutos devolve o job que já existe em vez de cobrar de novo.
72
74
 
73
75
  ## Troubleshooting
package/dist/canal.js ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Por onde este processo chegou na máquina da pessoa. Muda como se atualiza:
3
+ *
4
+ * - "npm" (padrão): `npx -y sapiens-mcp` no config do cliente. Versão nova =
5
+ * limpar o cache do npx ou pinar a versão.
6
+ * - "mcpb": a extensão do Claude Desktop (tools/mcp-sapiens/scripts/mcpb.mjs).
7
+ * O manifesto dela põe SAPIENS_MCP_CANAL=mcpb no ambiente. Versão nova =
8
+ * baixar o arquivo de novo e abrir: o Claude Desktop troca a extensão.
9
+ */
10
+ export const CANAL = process.env.SAPIENS_MCP_CANAL === "mcpb" ? "mcpb" : "npm";
11
+ /**
12
+ * Endereço estável da extensão no CDN da casa. O scripts/mcpb.mjs sobe aqui a
13
+ * cada release e purga o cache da borda; a página /conectar-claude aponta pro
14
+ * mesmo arquivo.
15
+ */
16
+ export const MCPB_URL = "https://sapiensinteticos.b-cdn.net/mcpb/sapiens.mcpb";
@@ -2,6 +2,8 @@ import { z } from "zod";
2
2
  import { convexQuery, convexMutation, convexAction, getSessionToken, saveSessionToken, clearSessionToken, describeConvexError, isRemoteContext, } from "../convexClient.js";
3
3
  import { getMcpVersion } from "../version.js";
4
4
  import { setTierFromIsAdmin } from "../tier.js";
5
+ import { CANAL, MCPB_URL } from "../canal.js";
6
+ import { valorDeChave } from "./pontes.js";
5
7
  /**
6
8
  * Versão do MCP realmente rodando (fonte única em ../version.js, lê o
7
9
  * package.json em runtime). Serve pra flagrar client preso em cache antigo do npx.
@@ -316,7 +318,9 @@ export async function meta(args) {
316
318
  running: ver.version,
317
319
  latest: ver.latest,
318
320
  howToFix: [
319
- `Atualizar o pacote: pinar sapiens-mcp@${ver.latest} na config do MCP, ou limpar o cache do npx e reiniciar o agent (restart sozinho não basta, o cache sobrevive).`,
321
+ CANAL === "mcpb"
322
+ ? `Baixar a extensão nova (${MCPB_URL}) e abrir o arquivo: o Claude Desktop troca a versão da extensão e as chaves já guardadas ficam.`
323
+ : `Atualizar o pacote: pinar sapiens-mcp@${ver.latest} na config do MCP, ou limpar o cache do npx e reiniciar o agent (restart sozinho não basta, o cache sobrevive).`,
320
324
  "Ou conectar pelo MCP remoto em sapiensinteticos.com/conectar-claude: ele sai do site, então está sempre na última versão e nunca desatualiza.",
321
325
  ],
322
326
  note: "AVISE O USUÁRIO já na primeira resposta. Client atrasado chama rotas antigas e falha de um jeito que parece erro do Sapiens (ex: filme que 'foi adicionado' e não entrou no Repertório).",
@@ -447,9 +451,12 @@ export async function meta(args) {
447
451
  version,
448
452
  latest,
449
453
  upToDate,
454
+ canal: CANAL,
450
455
  runtime: `node ${process.version}`,
451
456
  note: latest && upToDate === false
452
- ? `Rodando ${version}, mas a última no npm é ${latest}: o client está ATRASADO (provável cache do npx). Pra atualizar: pinar sapiens-mcp@${latest} na config do MCP ou recriar o container (restart sozinho não basta).`
457
+ ? CANAL === "mcpb"
458
+ ? `Rodando ${version} pela extensão do Claude Desktop, e a última é ${latest}. Pra atualizar: baixar a extensão nova (${MCPB_URL}) e abrir o arquivo; as chaves já guardadas ficam.`
459
+ : `Rodando ${version}, mas a última no npm é ${latest}: o client está ATRASADO (provável cache do npx). Pra atualizar: pinar sapiens-mcp@${latest} na config do MCP ou recriar o container (restart sozinho não basta).`
453
460
  : latest && upToDate
454
461
  ? `Na última versão (${version}).`
455
462
  : `Versão do binário rodando agora: ${version}. Não consegui consultar o npm pra comparar (offline/timeout).`,
@@ -554,10 +561,7 @@ export async function meta(args) {
554
561
  const remoto = isRemoteContext();
555
562
  const detectadas = remoto
556
563
  ? []
557
- : PONTES.filter((p) => p.envVars.some((nome) => {
558
- const valor = process.env[nome];
559
- return typeof valor === "string" && valor.trim().length >= 8;
560
- })).map((p) => p.key);
564
+ : PONTES.filter((p) => p.envVars.some((nome) => Boolean(valorDeChave(nome)))).map((p) => p.key);
561
565
  const naPortaLocal = detectadas.filter((k) => PORTA_LOCAL.includes(k));
562
566
  return {
563
567
  regra: "A chave de provedor NUNCA passa pelo Sapiens: ela mora no ambiente da máquina da pessoa e quem chama o motor é um processo de lá (o harness dela, ou o sapiens-mcp instalado pela sapiens_pontes). A casa entra com a ficha do personagem (sapiens_character action=get: passportPrompt + imageUrls) e recebe a peça de volta pelo ingest, que grava motor, prompt verbatim, custo real e personagem. Com a chave dela, gerar custa zero em Sinapses: ela paga o preço do provedor, sem margem da casa. Passo a passo: sapiens_skill action=get name=pontes.",
@@ -5,6 +5,7 @@ import path from "node:path";
5
5
  import { createHash, randomBytes } from "node:crypto";
6
6
  import { describeConvexError, getSessionToken, isRemoteContext, } from "../convexClient.js";
7
7
  import { findMinorMarkers } from "../pisoDeIdade.js";
8
+ import { CANAL } from "../canal.js";
8
9
  import { gallery } from "./gallery.js";
9
10
  /**
10
11
  * A PORTA LOCAL DAS PONTES (27/09/2026): gerar no provedor com a chave da
@@ -135,11 +136,26 @@ export const pontesSchema = z.object({
135
136
  // ---------------------------------------------------------------------------
136
137
  // A chave: lida do ambiente, nunca devolvida.
137
138
  // ---------------------------------------------------------------------------
139
+ /**
140
+ * O valor de uma variável de chave, ou undefined quando não há chave de verdade.
141
+ * Na extensão do Claude Desktop (.mcpb) a chave chega pelo manifesto como
142
+ * "${user_config.fal_key}": campo opcional deixado em branco pode chegar vazio
143
+ * OU como o próprio texto do molde, e o molde não é chave (mandar ele pro
144
+ * provedor seria um 401 com cara de chave errada).
145
+ */
146
+ export function valorDeChave(nome) {
147
+ const valor = process.env[nome]?.trim();
148
+ if (!valor || valor.length < 8)
149
+ return undefined;
150
+ if (/^\$\{[^}]*\}$/.test(valor))
151
+ return undefined;
152
+ return valor;
153
+ }
138
154
  function segredosConfigurados() {
139
155
  const out = new Set();
140
156
  for (const nome of NOMES_DE_CHAVE) {
141
- const valor = process.env[nome]?.trim();
142
- if (!valor || valor.length < 8)
157
+ const valor = valorDeChave(nome);
158
+ if (!valor)
143
159
  continue;
144
160
  out.add(valor);
145
161
  // A chave da fal é "id:segredo": um erro que ecoa só o id ainda identifica
@@ -163,9 +179,11 @@ function limpo(valor) {
163
179
  }
164
180
  function chaveDo(provedor) {
165
181
  const { nome, envVar } = BALCOES[provedor];
166
- const valor = process.env[envVar]?.trim();
167
- if (!valor || valor.length < 8) {
168
- throw new Error(`Não achei a ${envVar} nesta máquina. Ponha a chave da ${nome} no "env" do sapiens, no config do seu cliente MCP, e reinicie o cliente: ${ENDERECO_SUAS_CHAVES}. A chave fica no seu computador; nunca mande ela no chat.`);
182
+ const valor = valorDeChave(envVar);
183
+ if (!valor) {
184
+ throw new Error(CANAL === "mcpb"
185
+ ? `A extensão do Sapiens está sem a chave da ${nome}. No Claude Desktop, abra Configurações, Extensões, Sapiens Sintéticos, cole a chave no campo da ${nome} e salve. Ela fica guardada no seu computador; nunca mande ela no chat.`
186
+ : `Não achei a ${envVar} nesta máquina. Ponha a chave da ${nome} no "env" do sapiens, no config do seu cliente MCP, e reinicie o cliente: ${ENDERECO_SUAS_CHAVES}. A chave fica no seu computador; nunca mande ela no chat.`);
169
187
  }
170
188
  return valor;
171
189
  }
@@ -728,16 +746,15 @@ async function status(args) {
728
746
  function balcoes() {
729
747
  return {
730
748
  regra: "A chave mora no env do config do seu cliente MCP, nesta máquina, e sai daqui só pro provedor. A casa não recebe, não guarda e não vê. Gerar com a sua chave custa zero em Sinapses: você paga o preço do provedor, sem margem da casa.",
731
- balcoes: Object.keys(BALCOES).map((k) => {
732
- const v = process.env[BALCOES[k].envVar]?.trim();
733
- return {
734
- provedor: k,
735
- nome: BALCOES[k].nome,
736
- variavel: BALCOES[k].envVar,
737
- configurada: Boolean(v && v.length >= 8),
738
- };
739
- }),
740
- comoConfigurar: `No config do cliente MCP, no bloco do sapiens: "env": { "FAL_KEY": "<sua chave da fal>", "KIE_API_KEY": "<sua chave da Kie>" }. Depois reinicie o cliente. Passo a passo por cliente: ${ENDERECO_SUAS_CHAVES}`,
749
+ balcoes: Object.keys(BALCOES).map((k) => ({
750
+ provedor: k,
751
+ nome: BALCOES[k].nome,
752
+ variavel: BALCOES[k].envVar,
753
+ configurada: Boolean(valorDeChave(BALCOES[k].envVar)),
754
+ })),
755
+ comoConfigurar: CANAL === "mcpb"
756
+ ? "Na extensão do Claude Desktop: Configurações, Extensões, Sapiens Sintéticos. Os campos da fal e da Kie ficam ali, e o que você colar fica guardado no seu computador."
757
+ : `No config do cliente MCP, no bloco do sapiens: "env": { "FAL_KEY": "<sua chave da fal>", "KIE_API_KEY": "<sua chave da Kie>" }. Depois reinicie o cliente. No Claude Desktop, a extensão (.mcpb) põe a chave num campo, sem editar config. Passo a passo por cliente: ${ENDERECO_SUAS_CHAVES}`,
741
758
  ondeAcharOModelo: "fal: o id do endpoint na página do modelo em fal.ai/models (ex: 'fal-ai/flux/dev'). Kie: o 'model' da página do modelo no Market da Kie. O input é o corpo que a mesma página descreve.",
742
759
  outrosBalcoes: "Sogni, Krea, Magnific, WaveSpeed, Replicate e a própria placa seguem pelo seu harness + sapiens_gallery action=ingest (catálogo em sapiens_meta action=pontes).",
743
760
  };
@@ -50,7 +50,7 @@ const APP = "https://sapiensinteticos.com";
50
50
  const STUDIOS = {
51
51
  "helen-voice": {
52
52
  name: "Helen Voice (TTS)",
53
- description: "Text-to-speech via ElevenLabs ou Google TTS (Gemini). BYOK suportado. Voice cloning a partir de samples (UI-only). v1.4: speak via MCP.",
53
+ description: "Text-to-speech via ElevenLabs ou Google TTS (Gemini), sempre em Sinapses: a casa não recebe chave de provedor. Voice cloning a partir de samples (UI-only). v1.4: speak via MCP.",
54
54
  url: `${APP}/dashboard/personagens`,
55
55
  status: "stable",
56
56
  tags: ["audio", "tts", "elevenlabs", "google", "voice"],
package/dist/version.js CHANGED
@@ -11,9 +11,12 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
11
11
  *
12
12
  * Antes disto a versão vivia em 3 literais independentes (package.json + 2 no
13
13
  * index.ts) sincronizados por um regex no release.sh — um bump off-path
14
- * dessincronizava em silêncio. Agora package.json é a única fonte.
14
+ * dessincronizava em silêncio. Agora package.json é a única fonte (a extensão
15
+ * recebe o mesmo número, copiado dele no build).
15
16
  */
16
17
  export function getMcpVersion() {
18
+ if (typeof __SAPIENS_MCP_VERSION__ === "string")
19
+ return __SAPIENS_MCP_VERSION__;
17
20
  try {
18
21
  const pkgPath = path.resolve(__dirname, "..", "package.json");
19
22
  const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sapiens-mcp",
3
- "version": "1.79.0",
3
+ "version": "1.80.0",
4
4
  "mcpName": "com.sapiensinteticos/sapiens",
5
5
  "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.",
6
6
  "type": "module",
@@ -55,7 +55,9 @@
55
55
  "zod-to-json-schema": "^3.23.5"
56
56
  },
57
57
  "devDependencies": {
58
+ "@anthropic-ai/mcpb": "^2.1.2",
58
59
  "@types/node": "^22.10.0",
60
+ "esbuild": "^0.28.2",
59
61
  "tsx": "^4.19.2",
60
62
  "typescript": "^5.7.2"
61
63
  }