Notion

Interaja com a API do Notion para ler, criar e modificar conteúdo usando linguagem natural.

Documentação

Servidor MCP Notion — Conecte Claude, Cursor e VS Code ao Notion

npm version NPM Downloads License Model Context Protocol Stars

Dê ao seu IA acesso de leitura/escrita ao Notion com um token e um comando. Claude Code, Claude Desktop, Cursor, VS Code, Cline, Zed, qualquer coisa que fale MCP: ele pode criar páginas, consultar bancos de dados, anexar blocos, aplicar modelos, comentar e enviar arquivos, em linguagem natural.

O Notion tem seu próprio servidor MCP. Onde este se diferencia:

  • Ele autentica com um token, então roda sem interface. O MCP hospedado do Notion é somente OAuth e alguém precisa clicar em "Autorizar". Este funciona em CI, cron jobs, agentes em segundo plano e implantações auto-hospedadas.
  • Ele não gasta seu contexto com esquemas de ferramentas. O servidor oficial de código aberto carrega 24 esquemas de endpoints no contexto do modelo na conexão: 17.163 tokens, reenviados a cada requisição pelo resto da sessão. Este carrega três ferramentas, 1.005 tokens — 94% menos, 17× menor — e busca o esquema de uma operação apenas quando uma tarefa realmente o utiliza.
  • Ele também não gasta seu contexto com respostas. Lendo as mesmas páginas através de ambos os servidores, puxar o conteúdo de uma página custa 82% menos (26.071 → 4.568 tokens em uma página de 88 blocos), uma consulta de banco de dados de 25 linhas 81% menos, um objeto de página 68% menos. O JSON bruto do Notion é majoritariamente id/type wrappers, annotations, e blocos created_by/parent/icon, e nada disso chega ao modelo. Essa é a metade que se acumula, porque a superfície de ferramentas é paga uma vez e as respostas são pagas a cada chamada. Medido contra um fixture reproduzível, com as ressalvas declaradas →

Nada se perde para chegar lá: uma consulta de banco de dados retorna linhas planas de nome → valor em vez dos sacos properties brutos do Notion (5,3× mais leve no benchmark), e verbose: true dá a você a forma SDK intocada sempre que quiser — dentro de 4 tokens do que o servidor oficial retorna, que é como o benchmark prova que ambos estão lendo a mesma coisa. Mutações em lote com rollback atômico, chaves de idempotência, retry em limites de taxa e erros de validação auto-corrigíveis são integrados, e a comparação abaixo tem o resto.

Notion MCP Server on Glama

Início rápido

1. Obtenha um token do Notion. Abra app.notion.com/developers/tokens → + New token → dê um nome, escolha seu workspace → Create token → copie o valor ntn_…. Um Personal Access Token vê tudo que você pode ver, sem compartilhamento por página. (Página ausente ou vazia? Seu admin desativou PATs — veja alternativas de autenticação.)

Notion developer portal — the Personal access tokens page with the + New token button in the top right

2. Instale-o.

npx add-mcp notion-mcp-server --env NOTION_TOKEN=ntn_paste_your_token_here

add-mcp encontra os clientes MCP na sua máquina e escreve a configuração para os que você escolher: Claude Code, Claude Desktop, Cursor, VS Code, Codex, Gemini CLI, Cline, Windsurf, Zed e uma dúzia de outros. Adicione -g para instalar no nível do usuário em vez do projeto atual, -a claude-code para pular o seletor, --all para escrever todos os clientes de uma vez.

Mantenha a flag --env. Sem ela, a entrada é escrita sem token, e o servidor inicia e depois falha em toda chamada com erro de autenticação.

Ou instale manualmente: configuração JSON, Claude Code, Cursor, VS Code, Gemini CLI, Claude Desktop, Docker

Qualquer cliente que leia um bloco mcpServers (o ~/.cursor/mcp.json do Cursor, o claude_desktop_config.json do Claude Desktop, as configurações do Cline, Zed, Continue…):

{
  "mcpServers": {
    "notion": {
      "command": "npx",
      "args": ["-y", "notion-mcp-server"],
      "env": { "NOTION_TOKEN": "ntn_paste_your_token_here" }
    }
  }
}

Claude Code:

claude mcp add notion -s user \
  -e NOTION_TOKEN=ntn_paste_your_token_here \
  -- npx -y notion-mcp-server

O Claude Code fala o protocolo da era 2025 via stdio, a menos que seja instruído de outra forma. Defina MCP_PROTOCOL_NEGOTIATION=auto no ambiente dele e ele testa o MCP 2026-07-28 (requisições sem estado, dicas de cache em toda listagem). O servidor atende ambos.

Cursor: Install MCP Server — clique, depois substitua YOUR_NOTION_TOKEN na entrada gerada.

VS Code (modo agente do Copilot): Install in VS Code — o VS Code solicita o token e o armazena como entrada secreta.

Gemini CLI:

gemini extensions install https://github.com/awkoy/notion-mcp-server

O repositório inclui um gemini-extension.json, então isso instala como extensão: ele pede o token uma vez, mantém no chaveiro do sistema e inicia o servidor com npx.

Claude Desktop, sem Node.js: baixe notion-mcp-server.mcpb do release mais recente e clique duas vezes (ou arraste para Configurações → Extensões), depois cole seu token quando solicitado. Nunca editou um arquivo de configuração antes? O passo a passo detalhado não assume nada.

Docker / Podman / OrbStack:

claude mcp add notion -s user \
  -e NOTION_TOKEN=ntn_paste_your_token_here \
  -- docker run --rm -i -e NOTION_TOKEN ghcr.io/awkoy/notion-mcp-server:latest

A flag -i é obrigatória para stdio. A imagem é compatível com OCI, então Podman, OrbStack, colima, Rancher Desktop, Finch e nerdctl aceitam as mesmas flags. Para um contêiner HTTP de longa duração, veja Transporte Remoto / HTTP.

3. Experimente. Em um novo chat:

"Use o Notion para criar uma página chamada 'Olá do meu agente' e adicione uma lista de verificação com três coisas para tentar hoje."

Seu IA chama notion_write e responde com um link para a página ao vivo.

O que seu IA pode fazer com ele

  • "Encontre cada linha no meu banco de dados de Tarefas onde Status é 'Fazendo' e me diga quais estão atrasadas." — filtros where tipados, linhas achatadas
  • "Renomeie essas 50 páginas para a nova convenção." — uma chamada em lote, paralelismo de 10 vias, retry idempotente
  • "Crie uma página a partir do meu modelo 'Revisão semanal' e preencha este resumo."
  • "Reescreva aquela página de especificação: corrija os cabeçalhos e adicione um exemplo de código." — round-trip de markdown via get_page_markdown → edição → update_page_markdown
  • "Comente nas notas da reunião de ontem com um resumo de um parágrafo."
  • "Envie este diagrama para a página de design." — uploads de parte única e múltiplas partes
  • "Olhe o screenshot naquele relatório de bug e me diga o que está errado." — get_image entrega a imagem ao modelo

O catálogo completo está no menu de operações: 47 operações por trás de três ferramentas.

Qual MCP do Notion você deve usar?

Melhor paraAutenticaçãoHeadless / CINotas
MCP hospedado do Notion (mcp.notion.com)Chat interativo no claude.ai, ChatGPT, CursorOAuth (um humano precisa clicar; o Notion diz que autenticação não interativa está em desenvolvimento)❌Primeira parte, ~34 ferramentas de markdown (11 delas ferramentas de sessão do Custom Agent que precisam do Notion AI), algumas limitadas por plano
Servidor oficial de código aberto—Token✅O Notion o chama de descontinuado e "não mais mantido ativamente"; o repositório diz que pode "descontinuá-lo" e que issues e PRs não são monitorados ativamente
Este servidorAgentes, automação, CI, auto-hospedagem, cargas sensíveis a tokensToken (PAT)✅Mantido ativamente, design agente-primeiro

Para conversar com seu Notion na interface web do claude.ai, use o conector hospedado do Notion: é um clique. Use este servidor quando o agente roda sem supervisão, quando o custo de contexto importa, ou quando você quer semânticas de lote e idempotência e seu próprio host.

Comparação detalhada vs. o servidor oficial de código aberto
CapacidadeNotion MCP oficial (código aberto)Este servidor
Superfície de ferramentas24 ferramentas (uma por endpoint), 17.163 tokens carregados no contexto3 ferramentas, 1.005 tokens — 94% menos esquema na conexão
Tamanho da respostaEnvelope completo do Notion em toda leitura82% menos lendo os blocos de uma página, 81% em uma consulta de banco de dados de 25 linhas, 68% em um objeto de página, 71% em uma busca — mesmos objetos, ambos servidores, pares correspondentes
Operações cobertas~24 endpoints47 operações (mais um alias trash_page) em páginas, blocos, bancos de dados, fontes de dados, visualizações, modelos, comentários, usuários, arquivos
Mutações em loteNão documentado✅ Envelope universal { items: [...] }; até 10 em paralelo
Lotes atômicos + rollbackNão documentado✅ atomic: true aborta na primeira falha, arquiva com melhor esforço entidades criadas anteriormente
IdempotênciaNão documentado✅ idempotency_key — mesma chave + operação retorna o resultado em cache por 5 minutos
Tratamento de limite de taxa429s aparecem✅ Limitador de token-bucket (3 req/s padrão) + backoff exponencial, respeita Retry-After
Formas de respostaJSON SDK bruto do NotionShapers enxutos removem ruído por padrão; verbose: true opta por sair e retorna a forma bruta
Consultas de banco de dadosSaco properties bruto por linhaMapa achatado nome → primitivo (todos os 20+ tipos de propriedade) — 16.629 → 3.143 tokens na consulta de 25 linhas do benchmark
Escrevendo propriedadesJSON de propriedade completo do NotionValores simples: { Status: "Done", Due: "2026-10-01", Tags: ["a"] }, tipados a partir do esquema da fonte de dados (cache de 5 min); nomes e opções errados são rejeitados com os válidos
FiltrosJSON de filtro bruto do NotionAbreviação where tipada — { Status: "Done", Priority: { in: [...] }, OR: [...] } e sorts: ["-Due Date"]; filtros brutos ainda são aceitos
Campos desconhecidosRejeitadosIgnorados com uma entrada warnings nomeando o campo e os aceitos, então a chamada ainda roda
PaginaçãoCursores manuaispaginate: true opcional percorre next_cursor (limite ≈ 1000 itens)
Formato de transmissãoSerialização SDK padrãoJSON compacto — payloads ~30% menores
MarkdownFerramentas de markdown no nível de página✅ Aceito por create_page / append_blocks / update_block / comentários, mais round-trip completo (get_page_markdown / update_page_markdown), GFM completo
Modelos—✅ create_page de um modelo do Notion + descoberta list_data_source_templates
Visualizações de banco de dados—✅ listar / obter / consultar / criar / atualizar / excluir; query_view executa os filtros e ordenações armazenados de uma visualização e retorna linhas hidratadas
Uploads de arquivosNão na superfície de ferramentas documentada✅ Parte única e múltiplas partes (chunks de 5 MB), MIME inferido
Erros de validaçãoString de erro simplesAuto-corrigível: { code, message, path, issues, schema, example, fix } — corrigido em um round-trip
Versão da API do Notion—2026-03-11 fixada (fontes de dados, visualizações, modelos)

O que isso compra na prática: renomear 50 páginas é uma chamada notion_write com { items: [...], concurrency: 10 } em vez de 50 viagens pelo loop de raciocínio do agente, e a economia de tokens de prompt é a maior metade da vitória. O benchmark tem o método, o tokenizador, o controle que o valida, um pior caso honesto e os limites da própria amostra.

Configuração

Token: PAT ou integração interna

Ambos vão na mesma variável de ambiente NOTION_TOKEN; só muda onde você os obtém.

Personal Access Token (recomendado)Integração Interna (escopada)
Ondeapp.notion.com/developers/tokens → + New tokenapp.notion.com/developers/connections → + New connection
EscopoTudo que você pode verApenas páginas onde você clicou • • • → Connect → <integration>
AtritoNenhumUma etapa de Connect por página ou banco de dados
Use quandoPadrão: workspaces pessoais e de equipe, prototipagemUm admin exige escopo explícito por recurso, ou para bots de produção compartilhados

💡 A maioria dos erros object_not_found é a escolha errada de autenticação, não um bug: um token de Integração Interna que nunca foi Conectado à página. Mude para um PAT.

Detalhes do PAT: capacidades, expiração, revogação, fallback desativado por admin **Pode:** ler todas as páginas às quais você tem acesso; criar e atualizar páginas e bancos de dados onde você tem direitos de edição; comentar como você; enviar arquivos. **Não pode:** acessar páginas que você não pode ver, contornar permissões do workspace, agir como outro usuário ou alterar configurações de administrador. O escopo de um PAT é a sua conta, então se você perder o acesso a uma página, o PAT também perde. Emita tokens separados para cada colega.

Expiração: PATs expiram 1 ano após a criação (documentação do Notion). Defina um lembrete para o mês 11.

Revogação: app.notion.com/developers/tokens → Revogar ao lado do token, com efeito imediato. Administradores do workspace podem revogar o de qualquer pessoa em Configurações e membros → Conexões → Todos os tokens de acesso pessoal.

Administrador desativou PATs? Peça para ativá-los, ou crie uma Integração Interna em app.notion.com/developers/connections (+ Nova conexão) e • • • → Conectar a cada página que o agente deve acessar. Mesma variável de ambiente NOTION_TOKEN.

Referência oficial: Guia de PAT · Visão geral de autorização.

Variáveis de ambiente

Variável de ambienteObrigatóriaPadrãoSignificado
NOTION_TOKEN✅—PAT (ntn_…, recomendado) ou segredo de Integração Interna (secret_… / ntn_…)
NOTION_PAGE_ID——Pai padrão para create_page / create_database quando nenhum parent é passado (página → Compartilhar → Copiar link; a URL inteira ou o id de 32 caracteres funcionam)
NOTION_RATE_LIMIT—3Requisições/segundo para o limitador compartilhado (limite documentado por integração do Notion)
NOTION_READ_ONLY——true/1/yes desativa todas as operações de escrita em um único interruptor
NOTION_ALLOWED_OPERATIONS—allLista de permissões separada por vírgulas de operações ou predefinições de grupo — veja Restringindo operações
NOTION_BLOCKED_OPERATIONS——Lista de bloqueio separada por vírgulas (mesmo vocabulário); vence a lista de permissões
NOTION_CONFIRM_DESTRUCTIVE——true/1 mantém operações destrutivas habilitadas, mas pergunta primeiro — veja Restringindo operações
NOTION_UPLOAD_ROOT——Confina a fonte de upload_file's path a um diretório — veja Arquivos
NOTION_FILE_URLS—fullref substitui as URLs de arquivo assinadas do Notion (~1.650 caracteres, válidas por uma hora) em respostas enxutas por referências notion-file: curtas — veja Arquivos
HTTPS_PROXY / HTTP_PROXY——Roteia todo o tráfego de saída — chamadas da API do Notion e os downloads em get_image e na fonte url de upload_file — através de um proxy HTTP(S) (variáveis de ambiente padrão, minúsculas também aceitas)
NOTION_DAILY_LOG_PAGE_ID——Usado apenas pelo prompt MCP de registro diário

Variáveis de transporte HTTP (MCP_TRANSPORT, PORT, HOST, MCP_AUTH_TOKEN, …) estão em Transporte remoto / HTTP.

Atualizando da v1.x ou v2.x? Toda variável de ambiente ainda funciona sem alterações. A mudança é a superfície de ferramentas: as cinco ferramentas da v1, depois notion_execute da v2, tornaram-se notion_read + notion_write, e notion_describe permanece como estava. Clientes modernos redescobrem ferramentas automaticamente. Detalhes em MIGRATION.md.

Restringindo operações

NOTION_ALLOWED_OPERATIONS (lista de permissões) e NOTION_BLOCKED_OPERATIONS (lista de bloqueio) aceitam cada uma uma lista separada por vírgulas de predefinições de grupo ou nomes de operação exatos.

PredefiniçãoExpande para
readtoda operação não mutável
writetoda operação mutável
destructiveoperações cujo propósito é remoção (archive_page/trash_page, delete_block, batch_mixed_blocks, delete_comment, delete_view)
pages blocks databases data_sources views comments users filestoda operação nessa família, leitura e escrita
{ "env": { "NOTION_ALLOWED_OPERATIONS": "read" } }                       // read-only, the common case
{ "env": { "NOTION_BLOCKED_OPERATIONS": "destructive" } }                // everything except removals
{ "env": { "NOTION_ALLOWED_OPERATIONS": "read,append_blocks,add_page_comment" } }

Nomes não diferenciam maiúsculas de minúsculas, tokens desconhecidos são ignorados com um aviso, a lista de bloqueio vence, e uma lista de permissões que resolve para zero operações desativa tudo (falha fechada). Operações desativadas desaparecem dos enums operation das ferramentas, de notion_describe e do menu notion://operations, então nomear uma falha na validação antes de executar; quando nenhuma operação de escrita está habilitada, notion_write não é anunciado de forma alguma. Uma linha no stderr na inicialização diz o que foi resolvido. Verifique-a primeiro quando a configuração não se comportar:

Operation access: 22/48 enabled (allow=read; block=(none))

Confirmar em vez de bloquear. NOTION_CONFIRM_DESTRUCTIVE=true mantém operações destrutivas disponíveis e faz notion_write perguntar antes de executar uma, através de elicitação MCP: uma solicitação elicitation/create em clientes da era 2025, uma ida e volta input_required em clientes MCP 2026-07-28, onde a repetição carrega um requestState selado que só corresponde à chamada para a qual foi cunhado. Você recebe um diálogo sim/não nomeando a operação e seu alvo (a página, banco de dados, fonte de dados ou título do bloco quando uma recuperação pode buscá-lo em 5 s, caso contrário o id; para um lote, quantos itens).

Restaurações (restore_page, delete_database / delete_data_source com in_trash: false) e uma chamada batch_mixed_blocks sem entrada delete nunca solicitam confirmação, e uma operação bloqueada ainda é rejeitada com operation_not_allowed antes que alguém seja perguntado. Recusar, cancelar ou responder não e a chamada retorna confirmation_declined; as instruções do servidor dizem ao modelo para não tentar novamente e perguntar a você. Um cliente que não declarou a capacidade de elicitação recebe confirmation_unavailable em vez de uma execução silenciosa. Use um cliente que suporte elicitação, desdefina a variável ou bloqueie operações destrutivas completamente.

Referência por operação e limitações
DomínioLeituraEscrita
pagessearch_pages get_page get_page_markdowncreate_page set_page_title set_page_property set_page_properties update_page_markdown move_page restore_page archive_page† trash_page†
blocksget_block get_block_childrenappend_blocks update_block delete_block† batch_mixed_blocks†
databasesquery_databasecreate_database update_database delete_database†
data_sourceslist_data_sources get_data_source list_data_source_templatesupdate_data_source delete_data_source†
viewslist_views get_view query_viewcreate_view update_view delete_view†
commentslist_comments get_commentadd_page_comment add_discussion_comment update_comment delete_comment†
userslist_users get_user get_bot_user get_self—
fileslist_file_uploads get_file_upload get_file_url get_imageupload_file

† = também no grupo destructive.

Limitações. O controle é por operação, não por parâmetro: update_page_markdown é uma operação de escrita que pode substituir o corpo de uma página, e bloquear destructive não a desativa. Para uma implantação garantida sem mutação, use NOTION_ALLOWED_OPERATIONS=read ou NOTION_READ_ONLY=true. Prompts MCP ainda podem mencionar operações desativadas, mas a execução é rejeitada.

Arquivos

Envios. upload_file recebe seus bytes como base64, um url público, ou um path local que o servidor lê diretamente. Uma fonte path pode ler qualquer arquivo que o processo do servidor puder, então quando um modelo compõe o caminho, defina NOTION_UPLOAD_ROOT para confiná-lo: caminhos relativos resolvem dentro da raiz, e symlinks são resolvidos antes da verificação para que não possam apontar para fora dela.

URLs de arquivo. O Notion gera uma nova URL S3 assinada para cada arquivo hospedado em cada leitura: cerca de 1.650 caracteres (~500 tokens), válida por uma hora, diferente a cada vez, e fácil para um modelo pequeno estragar. NOTION_FILE_URLS=ref as substitui em respostas enxutas (get_page, search_pages, query_database, query_view, get_block, get_block_children, …) por referências curtas e estáveis.

ReferênciaNomesResolvido por
notion-file:block/<block-id>O arquivo em um bloco de imagemget_file_url → { ref, url }, uma URL assinada nova válida por cerca de uma hora
notion-file:page/<page-id>/<property>/<index>Uma entrada da propriedade files de uma página (nome da propriedade codificado em URL)get_image → a imagem como conteúdo de imagem MCP, para que o modelo possa vê-la (somente image/*, até 5 MB)

Ambos os resolvedores releem o objeto através da API do Notion, então uma referência permanece válida enquanto o arquivo existir. get_image busca apenas a URL que o Notion retornou para um arquivo hospedado no Notion, nunca uma fornecida pelo chamador, então não pode ser direcionada a um host LAN, um endpoint de metadados de nuvem ou um alvo de exfiltração. URLs externas (imagens vinculadas, arquivos external) já são curtas e estáveis: passam intactas em qualquer modo, e get_image as retorna como texto em vez de buscá-las. get_page_markdown é o markdown renderizado do próprio Notion e não é reescrito. O padrão, full, deixa cada resposta como estava.

Transporte remoto / HTTP

O servidor fala stdio por padrão. Defina MCP_TRANSPORT=http para executá-lo como um endpoint remoto, para clientes web, agentes em rede e implantações compartilhadas:

MCP_TRANSPORT=http PORT=3000 NOTION_TOKEN=ntn_xxx npx -y notion-mcp-server
# -> notion-mcp-server vX.Y.Z running on http://127.0.0.1:3000/mcp

Ele serve MCP Streamable HTTP em /mcp para ambas as gerações atuais de protocolo, escolhido por solicitação com base no que o cliente envia. Clientes MCP 2026-07-28 obtêm o caminho sem estado, onde cada POST é independente: sem sessão, server/discover, dicas de cache em cada lista. Clientes 2024-11-05 … 2025-11-25 obtêm sessões via cabeçalho mcp-session-id mais o stream GET e DELETE, e um GET/DELETE sem id de sessão recebe 405. Há também um GET /health não autenticado. O processo é single-tenant: cada solicitação age como o NOTION_TOKEN com o qual começou.

envpadrãosignificado
MCP_TRANSPORTstdiodefina para http para habilitar HTTP
PORT3000porta de escuta (0 = atribuída pelo SO)
HOST127.0.0.1endereço de bind; defina 0.0.0.0 para expor externamente (somente com MCP_AUTH_TOKEN)
MCP_AUTH_TOKEN—quando definido, toda solicitação /mcp deve enviar Authorization: Bearer <token>
MCP_ALLOWED_HOSTSlocalhost + host vinculadolista separada por vírgulas para lista de permissões Host de rebinding DNS
MCP_ALLOWED_ORIGINSorigens localhostlista separada por vírgulas para lista de permissões Origin do navegador

⚠️ Quem alcançar /mcp age como seu NOTION_TOKEN. Em loopback, o padrão, isso significa apenas processos locais. Antes de vincular um HOST não-loopback, defina MCP_AUTH_TOKEN (o servidor avisa se você não fizer) e coloque um proxy reverso autenticador na frente dele.

Conectando de um cliente que suporta cabeçalhos (Claude Code, Cursor, VS Code) e verificando localmente:

claude mcp add --transport http notion https://your-host/mcp \
  --header "Authorization: Bearer <MCP_AUTH_TOKEN>"

curl http://127.0.0.1:3000/health
# -> {"status":"healthy","transport":"http","port":3000}
npx @modelcontextprotocol/inspector --transport http --server-url http://127.0.0.1:3000/mcp

No Docker, HOST=0.0.0.0 é o que torna a porta publicada alcançável, já que dentro do contêiner 127.0.0.1 é o loopback do próprio contêiner. Um bind não-loopback é exatamente onde MCP_AUTH_TOKEN mostra seu valor:

docker run --rm -e NOTION_TOKEN=ntn_xxx -e MCP_TRANSPORT=http -e HOST=0.0.0.0 -e MCP_AUTH_TOKEN=change-me \
  -p 3000:3000 ghcr.io/awkoy/notion-mcp-server

Builds do Claude Desktop afetados por anthropics/claude-code#93290 enviam um corpo 2026-07-28 sob um cabeçalho MCP-Protocol-Version: 2025-11-25. O servidor realinha essa única incompatibilidade conhecida para que esses builds funcionem; toda outra divergência cabeçalho/corpo recebe a rejeição que a especificação prescreve (-32020).

Verificações de saúde para um contêiner HTTP A imagem não inclui um `HEALTHCHECK` porque ela inicia no modo stdio, onde nada escuta e uma sondagem embutida de `/health` marcaria cada contêiner stdio como não saudável. Adicione um você mesmo para uma implantação HTTP. O mesmo comando está comentado no `Dockerfile` e funciona como `--health-cmd` no `docker run` também:
services:
  notion-mcp-server:
    image: ghcr.io/awkoy/notion-mcp-server:latest
    environment:
      NOTION_TOKEN: ${NOTION_TOKEN:?NOTION_TOKEN is required}
      MCP_TRANSPORT: http
      HOST: 0.0.0.0
      MCP_AUTH_TOKEN: ${MCP_AUTH_TOKEN:?MCP_AUTH_TOKEN is required}
    ports: ["3000:3000"]
    healthcheck:
      test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:'+(process.env.PORT||3000)+'/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
      interval: 30s
      timeout: 3s
      start_period: 5s
      retries: 3

Ferramentas MCP

Três ferramentas, independentemente de qual das 47 operações você acabe chamando. notion_read executa as leituras, notion_write as escritas, e notion_describe retorna o JSON Schema de uma operação mais um exemplo funcional, o que vale uma ida e volta antes de uma chamada complexa: expressões de filtro, lotes de blocos mistos, definições de propriedades de banco de dados. O campo operation de cada ferramenta é um enum exatamente do que este servidor tem habilitado, então o menu acompanha a lista de ferramentas, um cliente pode validar uma chamada antes de enviá-la, e um nome enviado para a ferramenta errada falha em uma ida e volta com uma mensagem nomeando a correta.

Todo campo de id (page_id, block_id, database_id, view_id, …) também aceita uma URL do Notion, então cole o que Compartilhar → Copiar link lhe dá. O #fragment de um link de bloco é usado para campos block_id e o ?v= de um link de banco de dados para campos view_id.

// notion_read
{ "operation": "search_pages", "payload": { "query": "Q3 plan" } }
{ "operation": "get_page_markdown", "payload": { "page_id": "https://www.notion.so/Q3-plan-1f3c…" } }

// notion_write, single call
{ "operation": "set_page_title", "payload": { "page_id": "<page-id>", "title": "Q3 plan" } }

// notion_write, batch: every mutating op takes { items: [...], atomic?, concurrency?, idempotency_key? }
{
  "operation": "set_page_title",
  "payload": {
    "items": [{ "page_id": "<p1>", "title": "First" }, { "page_id": "<p2>", "title": "Second" }],
    "concurrency": 3,
    "idempotency_key": "rename-pass-2026-07-02"
  }
}

// markdown shortcut (create_page, append_blocks, update_block, update_page_markdown)
{
  "operation": "create_page",
  "payload": {
    "parent": { "type": "page_id", "page_id": "<parent>" },
    "title": "Notes",
    "markdown": "# Heading\n\n- [ ] todo\n- [x] done\n\n```ts\nconst x = 1;\n```"
  }
}

// a database row: plain property values, typed from the data source's schema
{
  "operation": "create_page",
  "payload": {
    "parent": { "type": "data_source_id", "data_source_id": "<data-source-id>" },
    "title": "Write the report",
    "properties": { "Status": "In Progress", "Due Date": "2026-10-01", "Tags": ["q3", "docs"] }
  }
}

// upload a file and place it on a page in one call
{
  "operation": "upload_file",
  "payload": {
    "source": { "type": "path", "path": "~/Desktop/chart.png" },
    "attach_to": { "block_id": "<page-or-block-id>", "caption": "Q3 revenue" }
  }
}

Um payload que não valida retorna com o JSON Schema completo da operação, um exemplo funcional e uma dica de fix, para que a próxima chamada possa ser corrigida sem uma ida e volta de notion_describe.

Permissões por ferramenta

Clientes MCP concedem permissão por nome de ferramenta, então a divisão leitura/escrita permite que você aprove leituras uma vez e mantenha escritas atrás de um prompt. No Claude Code (~/.claude/settings.json ou o .claude/settings.json do projeto, onde notion é o nome que você deu ao servidor):

{
  "permissions": {
    "allow": ["mcp__notion__notion_read", "mcp__notion__notion_describe"]
  }
}

As configurações MCP do Cursor oferecem a mesma lista de permissões por ferramenta. notion_read é anotado como readOnlyHint: true e notion_write como destructiveHint: true, para clientes que leem anotações.

Menu de operações (47 operações, mais um alias)

Leituras (get_*, list_*, search_pages, query_database, query_view) passam por notion_read, todo o resto por notion_write.

ÁreaOperações
Páginascreate_page, get_page, set_page_title, set_page_property, set_page_properties, archive_page (alias: trash_page), restore_page, search_pages, move_page, get_page_markdown, update_page_markdown
Blocosappend_blocks, get_block, get_block_children, update_block, delete_block, batch_mixed_blocks
Bancos de dadoscreate_database, query_database, update_database, delete_database
Fontes de dadoslist_data_sources, get_data_source, update_data_source, delete_data_source, list_data_source_templates
Visualizaçõeslist_views, get_view, query_view, create_view, update_view, delete_view
Comentárioslist_comments, add_page_comment, add_discussion_comment, get_comment, update_comment, delete_comment
Usuárioslist_users, get_user, get_bot_user, get_self
Arquivosupload_file, list_file_uploads, get_file_upload, get_file_url, get_image

A lista autoritativa, com capacidade de lote e a ferramenta que executa cada operação, é servida como um recurso MCP em notion://operations.

Recursos MCP

Clientes que suportam anexo de recursos (menção @) podem puxar conteúdo do Notion para o contexto sem uma chamada de ferramenta. Recursos dinâmicos passam pela mesma autenticação, limite de taxa e controle de acesso que chamadas de ferramenta.

URI do recursoRetorna
notion://operationsFolha de dicas em Markdown de toda operação habilitada
notion://page/<page_id>Corpo da página como markdown
notion://database/<data_source_id>Esquema da fonte de dados como JSON

Solução de problemas

  • object_not_found / "Could not find …" — um token de Integração Interna só vê páginas explicitamente Conectadas a ele. Troque para um PAT para pular o compartilhamento por página.
  • "Notion auth failed" em toda chamada — token ausente, revogado ou expirado (PATs duram um ano). Verifique NOTION_TOKEN na configuração do seu cliente, depois se o token ainda está Ativo em app.notion.com/developers/tokens. Instalado com add-mcp e pulou --env? A entrada não tem token; execute novamente com ele.
  • "No parent page configured" — passe parent na chamada, ou defina NOTION_PAGE_ID.
  • multi_source_database de query_database ou create_page — o banco de dados tem várias fontes de dados. Chame list_data_sources, depois passe data_source_id (ou um pai data_source_id) em vez de database_id.
  • Um resultado bem-sucedido carrega warnings — a chamada foi executada; cada entrada nomeia um campo que foi ignorado (com erro de grafia ou mal posicionado) ou um nome de propriedade que foi corrigido. Corrija o payload na próxima vez, nada para tentar novamente.
  • Ferramentas não aparecem no Claude Desktop — erro de digitação no token (ele deve permanecer dentro das aspas) ou o aplicativo não foi totalmente encerrado (Cmd+Q, não fechar a janela) antes de reabrir.
  • Os logs de inicialização mostram "Notion auth check failed" mas as ferramentas funcionam — a verificação de inicialização é de melhor esforço; ignore-a se as chamadas forem bem-sucedidas.
  • Docker sai imediatamente / "Connection closed" — a flag -i é obrigatória: docker run --rm -i ….
  • Docker: "NOTION_TOKEN is not set" apesar de -e — escreva -e NOTION_TOKEN (encaminha do ambiente pai) ou -e NOTION_TOKEN=ntn_xxx, não -e NOTION_TOKEN ntn_xxx.

Ainda travado? GitHub Issues · FAQ · Referência da API do Notion · Especificação MCP

Privacidade

O servidor roda na sua máquina ou no seu próprio host e fala apenas com api.notion.com, via HTTPS, com o token que você configura. Sem telemetria, sem análises, sem servidor nosso no caminho: nada que você leia ou escreva no Notion vai para qualquer outro lugar. O token permanece onde seu cliente MCP o mantém, no arquivo de configuração dele ou em um chaveiro para clientes que têm um. Com HTTPS_PROXY definido, o tráfego passa pelo seu proxy. get_image busca apenas as URLs assinadas que o Notion retorna para arquivos que ele hospeda, nunca uma URL fornecida pelo modelo, e upload_file lê um arquivo local apenas quando solicitado, dentro de NOTION_UPLOAD_ROOT quando isso está definido. O tratamento dos seus dados pelo próprio Notion é coberto pela política de privacidade do Notion.

Desenvolvimento

git clone https://github.com/awkoy/notion-mcp-server.git
cd notion-mcp-server
npm install
echo "NOTION_TOKEN=ntn_xxx" > .env

npm run build       # tsc -> build/
npm test            # vitest suite
npm run inspector   # MCP inspector against the built binary

Aponte um cliente para a build local em vez de npx:

claude mcp add notion -s user -e NOTION_TOKEN=ntn_xxx -- node "$(pwd)/build/index.js"

Os logs vão para stderr e também são enviados ao cliente como entradas MCP notifications/message (logger notion-mcp-server), então aparecem na própria visualização de logs do cliente — canal de saída do VS Code, MCP Inspector, logs do Claude Desktop — onde stderr geralmente fica oculto. Clientes de 2025 escolhem o nível com logging/setLevel (padrão info); clientes MCP 2026-07-28 não têm essa chamada e pedem por solicitação com a chave de envelope io.modelcontextprotocol/logLevel, então uma solicitação sem ela não recebe notificações de log. O stderr não é afetado de qualquer forma. Em debug você também recebe uma linha por chamada de notion_read / notion_write: operação, tamanho do lote, duração, ok ou erro, nunca o payload ou o conteúdo da página.

Detalhes técnicos: como é construído
  • TypeScript + MCP TypeScript SDK v2 (@modelcontextprotocol/server + @modelcontextprotocol/node 2.0.0); transportes stdio + Streamable HTTP; revisões de protocolo 2024-11-05 até 2026-07-28 (serveStdio / createMcpHandler para o caminho sem estado 2026-07-28, o transporte com sessão para o resto)
  • Notion SDK @notionhq/client@^5.22.0, fixado em Notion-Version: 2026-03-11
  • Validação de payload Zod 4; emite JSON Schema draft-7 com deduplicação $defs para envelopes de erro
  • Markdown → blocos do Notion via remark / remark-gfm
  • Trabalhador de lote com concorrência limitada (padrão 3, máximo 10); limitador de taxa compartilhado de token bucket; withRetry com backoff exponencial em torno de cada chamada despachada
  • Cache de idempotência em memória (TTL de 5 minutos, 512 entradas)
  • Shapers enxutos por tipo de entidade com opt-out verbose: true
  • Suíte Vitest cobrindo o parser de markdown, shapers, emissor de esquema, despachante, semântica de lote (sucesso parcial / rollback atômico / idempotência), controle de acesso e transporte HTTP
Teste de fumaça de ponta a ponta contra um workspace real

npm test roda contra um cliente Notion simulado. scripts/e2e.mjs dirige o servidor compilado via stdio contra um workspace real: toda operação de leitura, os recursos e prompts, notion_describe para toda operação, e, com --write, toda operação de escrita dentro de uma única página descartável.

npm run build
printf 'NOTION_TOKEN=ntn_...\nNOTION_PAGE_ID=<page the token can write under>\n' > .env   # gitignored
npm run e2e                      # read-only pass
npm run e2e -- --write           # full pass; creates one page under NOTION_PAGE_ID and trashes it at the end
npm run e2e -- --write --keep    # keep the test page for inspection
npm run e2e -- --modern          # any of the above as an MCP 2026-07-28 client (stateless envelope, input_required confirmations)

Ele imprime uma tabela PASS/FAIL por verificação, lista qualquer operação que a execução não alcançou e sai com código não zero em falha. Não faz parte do CI.

Contribuindo

PRs são bem-vindos. Fork → branch → commit → push → PR. Execute npm test antes de enviar.

Licença

MIT — veja LICENSE.


mcp-name: io.github.awkoy/notion-mcp-server