Substack MCP (unofficial)

Servidor MCP e CLI não oficial para criadores do Substack: rascunhos ricos em Markdown, tags de rascunho, Notes, análises de publicação e posts, busca no arquivo, leitura de perfil público e Notes, e segmentação de assinantes. Posts longos permanecem como rascunhos; Notes são publicados imediatamente. Não afiliado ao Substack.

Documentação

substack-mcp

Crie e gerencie seu boletim informativo do Substack a partir do seu assistente de IA ou terminal. Prepare rascunhos ricos, pesquise seu arquivo, publique Notes, inspecione análises e gerencie assinantes gratuitos com consentimento explícito em todas as publicações. Revise e publique posts longos no Substack.

License: MIT Language: TypeScript npm version MCP HOL Plugin Security Scan Podcast X


Create, search, export, plan and review a draft with substack-mcp

A demonstração executa handlers MCP reais contra dados de amostra offline. Nenhuma chamada de API ao vivo ou publicação ocorre. Siga o fluxo de rascunho para criar, encontrar, exportar e revisar um post.

Seguro por design — com uma exceção bem destacada: Este servidor não pode publicar ou excluir posts longos. As ferramentas de post criam e editam apenas rascunhos; você revisa e publica manualmente pelo editor do Substack. A exceção são as Notes do Substack: create_note e create_note_with_link publicam Notes curtas imediatamente, porque Notes não têm estado de rascunho no Substack. Trate as ferramentas de Note como ações de publicação pública — não há etapa de pré-visualização nem desfazer a partir deste servidor. A divisão é uma revisão proporcional, a peça de infraestrutura de confiança para agentes que este servidor mais valoriza: a superfície de alto risco recebe uma barreira humana, e a exceção é declarada em voz alta.

Este servidor não impõe verificação de status Bestseller. Use uma conta autenticada com permissão para gerenciar a publicação; operações individuais dependem do seu acesso ao Substack. Conecte-se via stdio local ou HTTP auto-hospedado.

substack-mcp MCP server

Para cobertura de configuração 1.0 e elegibilidade de conta, consulte compatibilidade. Mantenedores podem usar a lista de verificação de lançamento e o inventário de distribuição.

Conteúdo: Início rápido · Configuração · Ferramentas · CLI do operador · Fluxo de rascunho · Análises para rascunho · Exportação · Markdown · Múltiplas publicações · Transportes · Compatibilidade

Início rápido

  1. Instale e entre. O login pelo navegador precisa da dependência opcional Playwright:

    npm install @conorbronsdon/substack-mcp playwright
    npx playwright install chromium
    npx substack-mcp login https://yourblog.substack.com --user-id 12345
    

    Use o ID de usuário da sua própria conta. A sessão é salva somente após uma leitura autenticada limitada ser bem-sucedida. Para colar credenciais em vez disso, veja Opção B.

  2. Verifique uma leitura. npx substack-mcp doctor --json --check-auth faz uma leitura limitada por publicação. Confirma acesso de leitura, não seu ID de usuário ou permissão de escrita.

  3. Conecte seu cliente MCP. Adicione o servidor ao Claude Desktop ou Claude Code, ou use o plugin Codex. Variáveis de ambiente têm precedência; omita-as para usar a sessão de login pelo navegador armazenada. Depois pergunte ao seu assistente: "Quantos assinantes do Substack eu tenho?"

  4. Prepare um rascunho para revisão. Siga o fluxo de rascunho: create_draft, search_posts, export_draft, preflight_draft, depois plan_draft_update e update_draft. Posts longos permanecem como rascunhos não publicados até você publicá-los no editor do Substack. create_note e create_note_with_link publicam imediatamente.

Passo a passo da comunidade

O artigo de Jonathan Price, I used Codex to connect ChatGPT to Substack. Then it drafted this post., mostra o uso do Codex para instalar o MCP localmente, conectar o ChatGPT através do Secure MCP Tunnel da OpenAI e criar um rascunho privado para publicação manual. Ele usou a conexão para criar o rascunho do próprio guia.

O guia documenta a configuração dele de 18 de setembro de 2026 com a versão 1.2.0. O get_post 404 relatado está corrigido na 1.2.1. Interfaces de cliente e requisitos de acesso podem mudar; use as instruções de configuração abaixo para a configuração atual deste pacote. Este é um passo a passo da comunidade, não um serviço hospedado fornecido por este projeto.

Configuração

Requer Node.js 22 ou mais recente (CI cobre Node 22 e 24). Login pelo navegador requer adicionalmente Playwright.

Você pode fornecer credenciais de duas maneiras: colá-las como variáveis de ambiente (abaixo) ou executar o login pelo navegador opcional, que captura e armazena as credenciais para você.

Opção A — Login pelo navegador (opcional, sem copiar cookies manualmente)

Instale o servidor e a dependência opcional Playwright juntos em um diretório local de ferramentas, depois entre:

npm install @conorbronsdon/substack-mcp playwright
npx playwright install chromium
npx substack-mcp login https://yourblog.substack.com --user-id 12345

substack-mcp-login continua sendo um alias suportado. URL de publicação ausente e ID de usuário são solicitados. Forneça o ID de usuário da sua própria conta; a linha de assinatura de um autor de post não verifica sua identidade. O navegador abre para o login, incluindo qualquer CAPTCHA. Apenas um cookie aplicável à API da publicação é capturado, e uma leitura autenticada limitada deve ser bem-sucedida antes de salvar. Isso verifica acesso de leitura, não o ID de usuário configurado ou permissão de escrita.

Sem --profile, o login salva ~/.substack-mcp/session.json (substituição de diretório: SUBSTACK_MCP_HOME). O servidor usa esta sessão legada quando variáveis de ambiente de credenciais de publicação e SUBSTACK_PROFILES não estão definidas.

Armazenamento padrão (SUBSTACK_CREDENTIAL_STORE=file): sessões usam AES-256-GCM com uma chave derivada da conta do SO e da máquina. Permissões de arquivo solicitam 0600; o acesso no Windows também depende de ACLs de diretório. Este é um arquivo vinculado à máquina, não um chaveiro do SO ou cofre de segredos. Código executando como seu usuário do SO pode derivar a chave. Use credenciais de ambiente se seu cliente MCP gerencia segredos para você.

Chaveiro do SO opcional: defina SUBSTACK_CREDENTIAL_STORE=keychain tanto no processo de login quanto no ambiente do cliente MCP. macOS usa Keychain via /usr/bin/security; Linux precisa de libsecret e secret-tool além de um Secret Service desbloqueado; Windows usa Credential Manager através do PowerShell PasswordVault. Todos os três são exercitados com credenciais sintéticas pelo fluxo de trabalho CI keychain em executores hospedados no GitHub (um keychain temporário do macOS desbloqueado e uma sessão gnome-keyring no Linux); configurações de desktop com chaveiros bloqueados podem ainda solicitar. O login grava a conta selecionada no chaveiro, e o servidor a lê de lá. A seleção explícita de chaveiro nunca lê o arquivo criptografado como fallback. O chaveiro ajuda contra outros usuários do SO, discos copiados e alguns malwares limitados a acesso a arquivos. Código executando como seu usuário geralmente pode consultar o chaveiro. Mantenha a conta do SO e o código em execução confiáveis. No Linux, secret-tool lookup pode sair com código 1 sem mensagem de erro para uma entrada ausente ou um chaveiro bloqueado. Gravações nomeadas sem --force pesquisam e desbloqueiam primeiro, e recusam sobrescritas quando uma entrada é encontrada.

Perfis nomeados e migração

npx substack-mcp login https://yourblog.substack.com --user-id 12345 --profile work
npx substack-mcp profiles list
# Copy an existing legacy session without changing its file:
npx substack-mcp profiles migrate --name personal
# Copy a file session or named file profile into the keychain; source remains:
npx substack-mcp profiles migrate --to keychain
npx substack-mcp profiles migrate --to keychain --name work

Chaves começam com uma letra ASCII minúscula e contêm apenas letras minúsculas, dígitos e hífens, até 64 caracteres. Perfis existentes exigem --force explícito para substituir. A saída da lista contém chaves, status de legibilidade, origens de publicação e horários de salvamento de arquivos; exclui cookies e IDs de usuário. Perfis ilegíveis permanecem visíveis, mas não podem ser selecionados. O horário de salvamento registra persistência local, incluindo migração; não é horário de emissão ou expiração de token. A listagem é limitada a 32 perfis.

O armazenamento de perfis requer um sistema de arquivos local que suporte hard links (como NTFS ou um sistema de arquivos Linux típico), para que a criação possa instalar um arquivo criptografado completo sem sobrescrever um nome existente. FAT/exFAT e alguns mounts de rede não são suportados: defina SUBSTACK_MCP_HOME para um diretório local adequado. Não use --force para contornar um sistema de arquivos não suportado.

Defina SUBSTACK_PROFILES=work,personal no ambiente do seu cliente MCP para selecionar até 32 perfis distintos. Remova todas as variáveis de credenciais de publicação primeiro: combinar seleção de perfil com variáveis de credenciais legadas ou nomeadas é um erro, incluindo variáveis vazias. Perfis selecionados ausentes, corrompidos ou inválidos interrompem a inicialização; eles nunca fazem fallback para outra conta. Perfis no disco nunca são ativados por descoberta. Com múltiplos perfis, as ferramentas exigem uma chave de publicação explícita e leituras de CLI exigem --publication.

Para reverter, desdefina SUBSTACK_PROFILES e restaure sua configuração de ambiente anterior. A migração preserva a sessão legada byte a byte. Esses arquivos usam o formato de criptografia existente. profiles list lista perfis de arquivo; selecione perfis de chaveiro explicitamente com SUBSTACK_PROFILES após migração ou login. Execute substack-mcp status --json para diagnósticos de configuração offline ou substack-mcp doctor --check-auth --json para uma leitura limitada por conta selecionada.

Opção B — Obtenha suas credenciais manualmente

Abra seu Substack em um navegador, depois:

  1. Token de sessão: Navegue até sua publicação, abra DevTools → Application → Cookies → copie o valor de connect.sid (string codificada em URL começando com s%3A)
  2. ID de usuário: Use o ID numérico da sua conta do Substack conectada, a partir dos dados autenticados da sua conta. Não use o ID de linha de assinatura de um post de publicação: publicações podem ter múltiplos autores. Este servidor não verifica independentemente o ID fornecido.
  3. URL da publicação: Sua URL do Substack, incluindo domínio personalizado se você tiver um (por exemplo, https://newsletter.yourdomain.com ou https://yourblog.substack.com)

2. Configure seu cliente MCP

Claude Desktop

Adicione ao seu claude_desktop_config.json:

{
  "mcpServers": {
    "substack": {
      "command": "npx",
      "args": ["-y", "@conorbronsdon/substack-mcp"],
      "env": {
        "SUBSTACK_PUBLICATION_URL": "https://yourblog.substack.com",
        "SUBSTACK_SESSION_TOKEN": "your-session-token",
        "SUBSTACK_USER_ID": "your-user-id"
      }
    }
  }
}

A configuração do plugin Claude Code e Codex está disponível junto com a configuração manual do MCP. O marketplace do repositório e o plugin local são separados da aceitação em diretório curado ou do suporte hospedado do ChatGPT.

Claude Code

Adicione ao seu .mcp.json:

{
  "mcpServers": {
    "substack": {
      "command": "npx",
      "args": ["-y", "@conorbronsdon/substack-mcp"],
      "env": {
        "SUBSTACK_PUBLICATION_URL": "https://yourblog.substack.com",
        "SUBSTACK_SESSION_TOKEN": "your-session-token",
        "SUBSTACK_USER_ID": "your-user-id"
      }
    }
  }
}

3. Verifique

Pergunte ao seu assistente de IA: "Quantos assinantes do Substack eu tenho?"

Compatibilidade de saída de ferramentas, limites de resposta e versionamento estão documentados em o contrato de ferramentas.

Diagnósticos do operador

O CLI expõe comandos de operador e verificações de configuração através dos mesmos handlers MCP e resolução de credenciais que o servidor. drafts create escreve um rascunho privado; os outros comandos de operador leem:

substack-mcp status --json
substack-mcp doctor --json --check-auth
substack-mcp drafts list --limit 10

Comandos, o envelope JSON, códigos de saída, prazos de solicitação e limites de resposta estão documentados em CLI do operador e diagnósticos.

Ferramentas

Cada ferramenta declara anotações explícitas de efeitos colaterais MCP; veja anotações de ferramentas. As descrições das ferramentas carregam a redação autoritativa.

Leitura

FerramentaDescrição
get_subscriber_countObtenha a contagem atual de assinantes da sua publicação
list_subscribersLeia uma página limitada de registros privados de assinantes
search_subscribersFiltre e pagine registros privados de assinantes; campos opcionais de atividade, data, sinalizadores e receita
get_subscriberConsulte uma assinatura por e-mail exato; reconcilie adições pendentes
list_published_postsListe posts publicados com paginação
get_publicationLeia a identidade/configurações projetadas da publicação, verifique o host configurado e relate campos ausentes; não verifica identidade ou função da conta
list_publication_tagsLeia definições de tags, incluindo tags ocultas por padrão, com paginação local limitada
get_post_tagsResolva associações de tags de posts; preserva IDs não resolvidos e relata incerteza de identidade em respostas vazias. A cobertura de rascunhos é atualmente verificada ao vivo apenas para respostas vazias
search_postsPesquise um arquivo de publicação por consulta e status; páginas limitadas com metadados de continuação
plan_draft_updateRevise alterações propostas, pré-verificação e um recibo para detecção de desatualização de melhor esforço; sem gravações
export_draftMarkdown editável, corpo original exato, diagnósticos de conversão, pré-verificação e link do editor
preflight_draftVerificações somente leitura para título, público, estrutura do corpo, imagens e paywalls, com link do editor
list_draftsListe posts de rascunho
get_postObtenha o conteúdo completo de um post publicado por ID
get_draftObtenha o conteúdo completo de um rascunho por ID
get_post_commentsObtenha comentários em um post publicado
get_sectionsListe as seções (categorias) da sua publicação com seus IDs
get_post_analyticsObtenha as estatísticas de um post publicado (visualizações, aberturas, inscrições, assinaturas, reações) por ID
rank_postsClassifique posts por visualizações, aberturas, envios, taxas, inscrições, assinaturas, valor estimado ou data, mantendo valores nulos e ausentes distintos
get_publication_statsLeia o resumo do painel e métricas de publicação em intervalo, com estados ausentes e indisponíveis
get_growth_sourcesLeia atribuição de origem de crescimento limitada e eventos opcionais
list_scheduled_postsListe posts agendados para publicação futura (somente leitura; o agendamento permanece no editor do Substack)
get_user_profileLeia um perfil público mínimo de usuário por handle, anonimamente
get_profile_feedLeia uma página de feed de perfil público com continuação por cursor, anonimamente
get_note_threadLeia uma Nota pública, ancestrais e uma página de respostas, anonimamente
list_public_postsLeia uma página de arquivo público limitada, anonimamente
get_public_postLeia um post público anônimo por URL com status do corpo e sinalizadores de truncamento

Leitura pública

Estas cinco ferramentas usam um leitor anônimo separado. Ele envia apenas User-Agent e Accept, nunca o cookie de publicação configurado ou outras credenciais. A publicação configurada seleciona a origem padrão do arquivo; os chamadores podem fornecer uma origem de publicação HTTPS na lista de permissões. Hosts permitidos são substack.com, *.substack.com de um rótulo, origens de publicação configuradas e origens exatas em SUBSTACK_PUBLIC_READ_ORIGINS (origens HTTPS separadas por vírgula, sem portas, caminhos ou userinfo). Redirecionamentos são rejeitados. Uma publicação *.substack.com pode redirecionar para seu domínio personalizado (por exemplo, lenny.substack.com); leituras JSON não seguem esse redirecionamento. Adicione a origem HTTPS personalizada a SUBSTACK_PUBLIC_READ_ORIGINS e leia através dessa origem. Com múltiplas publicações, a chave publication permanece obrigatória para todas as ferramentas.

As páginas de feed de perfil são do tamanho do upstream; has_more: null significa que o upstream omitiu metadados de continuação. Páginas de tópicos podem omitir respostas quando more_branches ou next_cursor está presente; completeness: "unknown" significa que os metadados de continuação foram omitidos. Páginas completas de arquivo têm has_more: null porque nenhum total é retornado. O body_status de post público é uma heurística baseada em público e presença de corpo; não estabelece acesso total. O leitor anônimo não usa direitos de assinatura. Assinaturas do leitor e caixa de entrada não são suportadas: a sessão de publicação configurada recebeu 401 nas rotas de conta de leitor substack.com, que exigem uma sessão de leitor separada que este servidor não gerencia.

Pesquisa de arquivo e revisão de rascunho

search_posts aceita query (1–500 caracteres), status (published, drafts, ou scheduled, padrão published), offset (padrão 0) e limit (1–50, padrão 25). Ele faz uma solicitação de arquivo autenticada e retorna metadados projetados, returned, total, has_more e next_offset. Continue com next_offset e a mesma consulta/status. Quando o Substack omite o total e uma página está cheia, has_more é nulo (desconhecido); outra página pode estar vazia. O Substack controla correspondência e indexação: isso não é uma varredura de texto completo garantida. Use get_post ou get_draft para recuperar conteúdo completo. A paginação não é um instantâneo; edições concorrentes podem mover resultados entre páginas.

preflight_draft aceita draft_id, lê uma vez e retorna checks_passed, descobertas com gravidade/código/mensagem, contagens de conteúdo e um link do editor. Ele verifica título, público, forma JSON/corpo, wrappers de imagem e fontes HTTPS, e contagem de paywall e posicionamento de borda. Nós desconhecidos e imagens externas produzem avisos de revisão. Corpos com mais de dois milhões de caracteres, 10.000 nós ou profundidade 100 não são totalmente verificados; counts.complete é falso e verificações agregadas são ignoradas após um limite de varredura. Avisos de nós desconhecidos nomeiam até cinco tipos para revisão do editor. Esta é uma verificação estática focada, não validação completa do ProseMirror ou aprovação de publicação. Não busca links/imagens, verifica configurações de acesso ou prova renderização final. Revise o rascunho no Substack; nenhum conteúdo é modificado.

Ambas as ferramentas exigem publication quando múltiplas publicações estão configuradas.

Gravação (rascunhos privados; upload de imagem retorna URL pública)

FerramentaDescrição
create_draftCrie um novo rascunho a partir de markdown (privado)
update_draftAplique um recibo de alteração revisado; reverifique o estado não publicado e relate resultados de leitura
update_draft_tagsPlaneje ou altere tags de rascunho; simulação por padrão, somente rascunho, com uma leitura após gravações
upload_imageEnvie uma imagem para o CDN do Substack a partir de um arquivo, URI de dados ou URL HTTPS pública — retorna uma URL publicamente acessível (não listada)

Revisão antes de alterar um rascunho

Na versão 0.9, chame plan_draft_update, revise sua saída, depois chame update_draft com os mesmos campos e recibo retornado. Rascunhos publicados ou conhecidamente desatualizados são rejeitados. A corrida de leitura/gravação permanece; verifique os resultados de leitura e revise no Substack. A CLI compartilha este fluxo através de drafts plan e drafts apply. Veja alterações de rascunho e migração para exemplos e limites.

Publicação (Notas — públicas imediatamente)

FerramentaDescrição
create_notePublique uma Nota do Substack (formato curto, publica imediatamente)
create_note_with_linkPublique uma Nota com anexo de cartão de link (publica imediatamente)

Notas não têm estado de rascunho no Substack, então não há opção de rascunho primeiro para estas duas ferramentas.

Gerenciamento de assinantes

add_free_subscriber adiciona um leitor consentido ao boletim gratuito. É uma mudança de distribuição: esse leitor pode receber e-mails futuros do boletim. Ele pode solicitar um e-mail de boas-vindas com send_welcome_email: true (desativado por padrão). Nunca concede acesso pago ou substitui a supressão de endereços anteriormente cancelados pelo Substack. Suas anotações MCP o identificam como uma gravação externa (readOnlyHint: false, openWorldHint: true).

{"email":"reader@example.org","consent_confirmed":true,"consent_evidence":{"source":"booking:message-id","recorded_at":"2026-09-01T00:00:00Z"},"dry_run":true}

A simulação é o padrão. Após verificar o consentimento real do boletim, defina dry_run: false para executar. Adições ao vivo exigem a referência de origem e timestamp em consent_evidence; esta atestação é ecoada com a chave de publicação para auditoria e não substitui a verificação do registro de consentimento subjacente. Configurações de múltiplas publicações também exigem o seletor publication, assim como todas as outras ferramentas.

Os resultados distinguem existing, dry_run, verified, blocked e unverified, busy e retryable. busy não realiza gravação; aguarde a outra operação. retryable significa que autenticação ou limitação de taxa recusou a solicitação; resolva essa condição antes de tentar novamente explicitamente. Um reconhecimento de API vazio não é prova de adição. verified significa que uma consulta de assinatura exata foi bem-sucedida após a solicitação; não prova que esta solicitação criou originalmente a assinatura. Dados do painel podem atrasar. Um leitor ausente também pode ter cancelado anteriormente.

Para unverified, reverifique com get_subscriber; nunca repita automaticamente a adição. Para blocked, revise no Substack sem contornar a supressão. Chamadores automatizados devem persistir um registro de tentativas antes de enviar cada solicitação ao vivo. O guarda de duplicatas em memória do cliente não sobrevive a reinicializações ou sessões HTTP separadas. Mantenha identidades de assinantes e evidências de consentimento fora de repositórios compartilhados, prompts para serviços públicos não aprovados e logs de rotina.

Notas de implementação e verificação ao vivo: API de assinantes.

Para opt-ins de agendamento do Google Calendar, o auxiliar de sincronização de calendário fornece uma varredura limitada do Gmail, seleção da resposta mais recente, um registro de tentativas durável privado e reconciliação somente leitura após gravações incertas. O agendamento é uma etapa de configuração local explícita; instalar o MCP não inicia um trabalho em segundo plano.

Excluído intencionalmente

  • Publicar posts — Publicar posts de formato longo deve ser uma ação humana deliberada (Notas são a exceção documentada acima)
  • Excluir — Destrutivo demais para uma ferramenta de IA
  • Agendar — Use o editor do Substack para agendamento. (list_scheduled_posts lê o que você enfileirou lá, mas este servidor nunca cria, edita ou cancela um agendamento.)

Para um agendador sempre ativo com estado de nuvem durável e relatórios semanais por e-mail, veja Sincronização de Calendário na Nuvem.

Múltiplas publicações

Executando mais de uma publicação em um único servidor? Defina um trio SUBSTACK_PUB_<KEY>_* por publicação em vez das variáveis SUBSTACK_* simples. <KEY> é qualquer nome que você escolher (letras, dígitos, sublinhados) — ele se torna a chave minúscula e hifenizada da publicação, ex.: KEVIN_MULDOON → kevin-muldoon.

"env": {
  "SUBSTACK_PUB_KEVIN_MULDOON_PUBLICATION_URL": "https://kevinmuldoon.substack.com",
  "SUBSTACK_PUB_KEVIN_MULDOON_SESSION_TOKEN": "token-1",
  "SUBSTACK_PUB_KEVIN_MULDOON_USER_ID": "111",
  "SUBSTACK_PUB_SAPERE_PUBLICATION_URL": "https://sapere.substack.com",
  "SUBSTACK_PUB_SAPERE_SESSION_TOKEN": "token-2",
  "SUBSTACK_PUB_SAPERE_USER_ID": "222"
}

Cada trio é independente, e definir qualquer variável SUBSTACK_PUB_<KEY>_* declara essa publicação. Um trio incompleto — uma variável ausente, um valor vazio ou um valor apenas com espaços — falha na inicialização com um erro nomeando a chave, em vez de descartar silenciosamente essa publicação. Isso importa porque uma publicação descartada não é "uma publicação a menos": descarte a única e o servidor volta para sua sessão de login de navegador armazenada; descarte uma de duas e toda ferramenta perde seu parâmetro publication, então uma chamada destinada à publicação descartada roteia silenciosamente para a sobrevivente.

As chaves são comparadas sem diferenciar maiúsculas de minúsculas, com _ dobrado para -. Dois nomes que resolvem para a mesma chave (SUBSTACK_PUB_ALPHA_* e SUBSTACK_PUB_Alpha_*) também são um erro de inicialização — mesclá-los silenciosamente permitiria que a URL de uma publicação se emparelhasse com o token de sessão de outra. <KEY> aceita letras ASCII, dígitos e sublinhados; os três sufixos devem estar em maiúsculas e o nome inteiro não pode ter espaços em branco soltos. Qualquer coisa que comece com SUBSTACK_PUB_ mas não se encaixe nesse formato — um hífen na chave, um sufixo em minúsculas, um caractere acentuado, um espaço no final — é um erro de inicialização que nomeia a variável, não uma variável que é ignorada silenciosamente. Pelo mesmo motivo acima: uma publicação ignorada não é uma publicação a menos, é um redirecionamento silencioso para outra.

Com duas ou mais publicações configuradas, toda ferramenta ganha um parâmetro publication obrigatório — uma das suas chaves configuradas (ex.: kevin-muldoon, sapere acima). O modelo chamador deve especificar uma em cada chamada; um valor não reconhecido é rejeitado antes de qualquer chamada à API do Substack, então uma escrita perdida não pode cair na publicação errada. Com exatamente uma publicação configurada — o caso comum, seja via variáveis SUBSTACK_* simples ou um trio SUBSTACK_PUB_<KEY>_* único — nenhum parâmetro publication é adicionado; o esquema de toda ferramenta permanece inalterado em relação ao modo de publicação única.

Não misture os dois estilos: se qualquer variável SUBSTACK_PUB_<KEY>_* estiver definida, as variáveis SUBSTACK_* simples são ignoradas (com um aviso na inicialização) em vez de serem tratadas como uma publicação extra sem nome.

SUBSTACK_USER_AGENT e SUBSTACK_REQUEST_TIMEOUT_MS se aplicam a todas as publicações configuradas — não são por publicação. O login pelo navegador também suporta perfis nomeados explícitos; veja Perfis nomeados e migração.

Expiração de token

Os tokens de sessão do Substack expiram periodicamente (tipicamente ~90 dias). Se você receber erros de autenticação, pegue um cookie connect.sid novo do seu navegador e atualize a variável de ambiente (certifique-se de que os bloqueadores de anúncios estejam desativados ao copiar o cookie) — ou, se você usou o login pelo navegador, basta executar substack-mcp-login novamente para atualizar a sessão armazenada.

Domínios personalizados e Cloudflare

Esta seção cobre chamadas autenticadas da API do criador. A leitura pública anônima usa as regras de origem de leitura pública acima.

Publicações do Substack servidas em um domínio personalizado (ex.: blog.example.com) ficam atrás do Cloudflare, que pode rejeitar requisições que não sejam de navegador com 403 error code: 1010. Para evitar isso, o servidor envia um User-Agent de navegador e um Referer por padrão, e endereça a publicação pelo host canônico *.substack.com.

  • Use o host canônico. Defina SUBSTACK_PUBLICATION_URL para o endereço *.substack.com da publicação em vez do domínio personalizado. Chamadas ao host canônico são servidas diretamente; chamadas ao domínio personalizado podem redirecionar com 301 e depois retornar 401.
  • Substitua o User-Agent (opcional) via SUBSTACK_USER_AGENT se você precisar de uma assinatura de navegador diferente:
"env": {
  "SUBSTACK_PUBLICATION_URL": "https://yourblog.substack.com",
  "SUBSTACK_SESSION_TOKEN": "your-session-token",
  "SUBSTACK_USER_ID": "your-user-id",
  "SUBSTACK_USER_AGENT": "Mozilla/5.0 ..."
}

Tempo limite de requisição

Toda requisição ao Substack é limitada por um prazo de 30 segundos. O Node não aplica tempo limite de requisição próprio — apenas um tempo limite de conexão de 10 segundos — então um host que aceita a conexão e depois fica em silêncio (um proxy que descarta pacotes em vez de recusá-los) travaria uma chamada de ferramenta indefinidamente. Uma requisição que atinge o prazo falha com um TimeoutError nomeando o endpoint e o limite.

Aumente ou diminua com SUBSTACK_REQUEST_TIMEOUT_MS (milissegundos; um valor não numérico ou não positivo é ignorado com um aviso e o padrão é usado):

"env": {
  "SUBSTACK_REQUEST_TIMEOUT_MS": "60000"
}

Transportes

Por padrão, o servidor fala MCP sobre stdio, que as configurações de cliente acima assumem. Defina MCP_TRANSPORT=http para um servidor HTTP Streamable sem estado (POST /mcp, GET /health) em uma implantação persistente e auto-hospedada. Configuração, variáveis do listener e o modelo de segurança estão em Transporte HTTP.

O que este listener aceitará

O listener HTTP inicia fechado: listas de permissão Host e Origin de loopback por padrão, um token bearer opcional (MCP_HTTP_TOKEN) e um limite de corpo de 10 MiB. As verificações de Host e Origin não são autenticação; defina um token onde outros processos possam alcançar a porta. Veja a política do listener.

Imagens GHCR versionadas e verificação de transporte: distribuição de contêineres.

Erros tipados

Falhas de API mapeiam para erros tipados (AuthenticationError, RateLimitError, ValidationError, NotFoundError, ServerError, TimeoutError e o base SubstackAPIError). O mapeamento de status e a análise do corpo do erro estão documentados em erros tipados.

Exportação de rascunho

Use export_draft para um pacote Markdown/JSON somente leitura, ou execute:

substack-mcp export 42 --output draft-export.json
substack-mcp export 42 --format markdown --output draft.md

As exportações Markdown retêm o corpo original exato em um arquivo auxiliar .source.json. Inspecione unsupported_nodes antes da reutilização. Arquivos existentes exigem --force. Veja comportamento de exportação e CLI para seleção de publicação, limites, exportações parciais e recuperação de arquivos.

Suporte a Markdown

Rascunhos aceitam Markdown CommonMark/GFM: títulos, negrito/itálico/tachado aninhados, links e links de referência, imagens com legendas e destinos vinculados, listas aninhadas com números iniciais, código, citações em bloco, regras e quebras de linha forçadas. Um bloco <!-- paywall --> independente adiciona um paywall a um rascunho de formato longo.

Conteúdo não suportado retorna unsupported_nodes antes de uma escrita. Após revisar esses diagnósticos, chamadores de rascunho podem definir explicitamente allow_unsupported: true para reter fallbacks literais. Tabelas permanecem Markdown dentro de blocos de código; tabelas nativas, callouts e embeds arbitrários não são anunciados como suportados. Notas de rodapé em parágrafos de nível superior mapeiam para as notas de rodapé nativas do editor em rascunhos de formato longo. Notas rejeitam conversão não suportada antes da criação da publicação ou do anexo e não têm substituição de fallback.

Veja Autoria em Markdown para mapeamentos, limites, mudanças de compatibilidade e a distinção entre fixtures offline e verificações do editor ao vivo.

Notas importantes

  • Este servidor usa a API não oficial do Substack. Pode quebrar se o Substack mudar seus endpoints.
  • Tokens de sessão são enviados como cookies. Mantenha seu SUBSTACK_SESSION_TOKEN seguro.
  • O servidor verifica suas credenciais na inicialização, após o handshake do MCP ser concluído, e apenas avisa — nunca bloqueia a inicialização em uma chamada de rede. As ferramentas ainda erram individualmente se o token estiver expirado, que é onde a falha é acionável.
  • SIGTERM e SIGINT são tratados: o servidor fecha seu transporte e sai com código 0, então docker stop retorna prontamente em vez de esperar o período de graça.

Desenvolvimento

Para verificações de leitura ao vivo opcionais, veja evidência de contrato ao vivo. A sonda é desativada em CI comum e nunca publica ou escreve.

Antes de lançar, execute npm run test:package. Ele instala o tarball compilado com dependências de produção em um diretório temporário limpo, verifica ambos os entrypoints executáveis e valida a versão do MCP e o catálogo completo de ferramentas registradas sem credenciais reais.

git clone https://github.com/conorbronsdon/substack-mcp.git
cd substack-mcp
npm install
npm run build

Execute localmente:

SUBSTACK_PUBLICATION_URL=https://yourblog.substack.com \
SUBSTACK_SESSION_TOKEN=your-token \
SUBSTACK_USER_ID=your-id \
npm start

Contribuindo

Issues e pull requests são bem-vindos. Como este servidor usa a API não oficial do Substack, as contribuições mais úteis são correções quando um endpoint muda. Se uma ferramenta parar de funcionar, abra uma issue com o nome da ferramenta e o erro. O limite seguro por design permanece: sem publicar, sem excluir, sem agendar posts de formato longo. Notas publicam imediatamente por design e devem continuar dizendo isso claramente em suas descrições.

Sobre

Construído e mantido por Conor Bronsdon para o fluxo de trabalho de produção do podcast Chain of Thought, onde ele redige e revisa posts de newsletter antes de um humano apertar publicar. Conor apresenta o Chain of Thought, um programa sobre infraestrutura de IA e como profissionais realmente constroem com ela. Mais ferramentas para criadores estão em ai-tools-for-creators. Encontre Conor no X em @ConorBronsdon.

Ferramentas complementares:

  • Transistor MCP: servidor MCP oficial do Transistor.fm. Episódios, publicação e análises.
  • podcastindex-mcp: pesquise o Podcast Index e acompanhe aparições de convidados
  • op3-mcp: relate downloads, geografia de ouvintes e aplicativos do OP3
  • apple-podcasts-mcp: obtenha reproduções, seguidores e escuta por episódio do Apple Podcasts Connect
  • gsc-mcp: consulte desempenho de pesquisa, palavras-chave e sitemaps no Google Search Console
  • podcast-benchmark: compare um programa com seus pares usando apenas dados públicos

Aviso legal

Este é um projeto pessoal independente, não afiliado, patrocinado ou endossado por nenhuma empresa. Todas as opiniões expressas são minhas.

Plugin Codex

O repositório inclui um manifesto Codex em .codex-plugin/plugin.json e uma configuração MCP em .mcp.json. Ele executa o pacote npm publicado via stdio usando npx; Node.js e npm devem estar disponíveis. A versão do pacote é fixada em .mcp.json, então atualizar o servidor do plugin é uma mudança explícita.

Configure suas credenciais do Substack fora do plugin usando as variáveis de ambiente ou a sessão de login pelo navegador descritas acima. Nunca faça commit de um token de sessão. A instalação não autentica uma conta nem concede aprovação para publicar. Posts de formato longo permanecem como rascunhos; Notas publicam imediatamente.

Licença

MIT