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.

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.
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
-
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 12345Use 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.
-
Verifique uma leitura.
npx substack-mcp doctor --json --check-authfaz uma leitura limitada por publicação. Confirma acesso de leitura, não seu ID de usuário ou permissão de escrita. -
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?"
-
Prepare um rascunho para revisão. Siga o fluxo de rascunho:
create_draft,search_posts,export_draft,preflight_draft, depoisplan_draft_updateeupdate_draft. Posts longos permanecem como rascunhos não publicados até você publicá-los no editor do Substack.create_noteecreate_note_with_linkpublicam 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:
- 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 coms%3A) - 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.
- URL da publicação: Sua URL do Substack, incluindo domínio personalizado se você tiver um (por exemplo,
https://newsletter.yourdomain.comouhttps://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
| Ferramenta | Descrição |
|---|---|
get_subscriber_count | Obtenha a contagem atual de assinantes da sua publicação |
list_subscribers | Leia uma página limitada de registros privados de assinantes |
search_subscribers | Filtre e pagine registros privados de assinantes; campos opcionais de atividade, data, sinalizadores e receita |
get_subscriber | Consulte uma assinatura por e-mail exato; reconcilie adições pendentes |
list_published_posts | Liste posts publicados com paginação |
get_publication | Leia 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_tags | Leia definições de tags, incluindo tags ocultas por padrão, com paginação local limitada |
get_post_tags | Resolva 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_posts | Pesquise um arquivo de publicação por consulta e status; páginas limitadas com metadados de continuação |
plan_draft_update | Revise alterações propostas, pré-verificação e um recibo para detecção de desatualização de melhor esforço; sem gravações |
export_draft | Markdown editável, corpo original exato, diagnósticos de conversão, pré-verificação e link do editor |
preflight_draft | Verificações somente leitura para título, público, estrutura do corpo, imagens e paywalls, com link do editor |
list_drafts | Liste posts de rascunho |
get_post | Obtenha o conteúdo completo de um post publicado por ID |
get_draft | Obtenha o conteúdo completo de um rascunho por ID |
get_post_comments | Obtenha comentários em um post publicado |
get_sections | Liste as seções (categorias) da sua publicação com seus IDs |
get_post_analytics | Obtenha as estatísticas de um post publicado (visualizações, aberturas, inscrições, assinaturas, reações) por ID |
rank_posts | Classifique posts por visualizações, aberturas, envios, taxas, inscrições, assinaturas, valor estimado ou data, mantendo valores nulos e ausentes distintos |
get_publication_stats | Leia o resumo do painel e métricas de publicação em intervalo, com estados ausentes e indisponíveis |
get_growth_sources | Leia atribuição de origem de crescimento limitada e eventos opcionais |
list_scheduled_posts | Liste posts agendados para publicação futura (somente leitura; o agendamento permanece no editor do Substack) |
get_user_profile | Leia um perfil público mínimo de usuário por handle, anonimamente |
get_profile_feed | Leia uma página de feed de perfil público com continuação por cursor, anonimamente |
get_note_thread | Leia uma Nota pública, ancestrais e uma página de respostas, anonimamente |
list_public_posts | Leia uma página de arquivo público limitada, anonimamente |
get_public_post | Leia 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)
| Ferramenta | Descrição |
|---|---|
create_draft | Crie um novo rascunho a partir de markdown (privado) |
update_draft | Aplique um recibo de alteração revisado; reverifique o estado não publicado e relate resultados de leitura |
update_draft_tags | Planeje ou altere tags de rascunho; simulação por padrão, somente rascunho, com uma leitura após gravações |
upload_image | Envie 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)
| Ferramenta | Descrição |
|---|---|
create_note | Publique uma Nota do Substack (formato curto, publica imediatamente) |
create_note_with_link | Publique 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_postslê 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_URLpara o endereço*.substack.comda 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_AGENTse 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_TOKENseguro. - 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.
SIGTERMeSIGINTsão tratados: o servidor fecha seu transporte e sai com código 0, entãodocker stopretorna 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