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
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/typewrappers,annotations, e blocoscreated_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.
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.)
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: — clique, depois substitua
YOUR_NOTION_TOKEN na entrada gerada.
VS Code (modo agente do Copilot): — 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
wheretipados, 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_imageentrega 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 para | Autenticação | Headless / CI | Notas | |
|---|---|---|---|---|
MCP hospedado do Notion (mcp.notion.com) | Chat interativo no claude.ai, ChatGPT, Cursor | OAuth (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 servidor | Agentes, automação, CI, auto-hospedagem, cargas sensíveis a tokens | Token (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
| Capacidade | Notion MCP oficial (código aberto) | Este servidor |
|---|---|---|
| Superfície de ferramentas | 24 ferramentas (uma por endpoint), 17.163 tokens carregados no contexto | 3 ferramentas, 1.005 tokens — 94% menos esquema na conexão |
| Tamanho da resposta | Envelope completo do Notion em toda leitura | 82% 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 endpoints | 47 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 lote | Não documentado | ✅ Envelope universal { items: [...] }; até 10 em paralelo |
| Lotes atômicos + rollback | Não documentado | ✅ atomic: true aborta na primeira falha, arquiva com melhor esforço entidades criadas anteriormente |
| Idempotência | Não documentado | ✅ idempotency_key — mesma chave + operação retorna o resultado em cache por 5 minutos |
| Tratamento de limite de taxa | 429s aparecem | ✅ Limitador de token-bucket (3 req/s padrão) + backoff exponencial, respeita Retry-After |
| Formas de resposta | JSON SDK bruto do Notion | Shapers enxutos removem ruído por padrão; verbose: true opta por sair e retorna a forma bruta |
| Consultas de banco de dados | Saco properties bruto por linha | Mapa achatado nome → primitivo (todos os 20+ tipos de propriedade) — 16.629 → 3.143 tokens na consulta de 25 linhas do benchmark |
| Escrevendo propriedades | JSON de propriedade completo do Notion | Valores 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 |
| Filtros | JSON de filtro bruto do Notion | Abreviação where tipada — { Status: "Done", Priority: { in: [...] }, OR: [...] } e sorts: ["-Due Date"]; filtros brutos ainda são aceitos |
| Campos desconhecidos | Rejeitados | Ignorados com uma entrada warnings nomeando o campo e os aceitos, então a chamada ainda roda |
| Paginação | Cursores manuais | paginate: true opcional percorre next_cursor (limite ≈ 1000 itens) |
| Formato de transmissão | Serialização SDK padrão | JSON compacto — payloads ~30% menores |
| Markdown | Ferramentas 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 arquivos | Não na superfície de ferramentas documentada | ✅ Parte única e múltiplas partes (chunks de 5 MB), MIME inferido |
| Erros de validação | String de erro simples | Auto-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) | |
|---|---|---|
| Onde | app.notion.com/developers/tokens → + New token | app.notion.com/developers/connections → + New connection |
| Escopo | Tudo que você pode ver | Apenas páginas onde você clicou • • • → Connect → <integration> |
| Atrito | Nenhum | Uma etapa de Connect por página ou banco de dados |
| Use quando | Padrão: workspaces pessoais e de equipe, prototipagem | Um 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 ambiente | Obrigatória | Padrão | Significado |
|---|---|---|---|
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 | — | 3 | Requisiçõ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 | — | all | Lista 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 | — | full | ref 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_executeda v2, tornaram-senotion_read+notion_write, enotion_describepermanece 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ção | Expande para |
|---|---|
read | toda operação não mutável |
write | toda operação mutável |
destructive | operaçõ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 files | toda 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ínio | Leitura | Escrita |
|---|---|---|
pages | search_pages get_page get_page_markdown | create_page set_page_title set_page_property set_page_properties update_page_markdown move_page restore_page archive_page† trash_page† |
blocks | get_block get_block_children | append_blocks update_block delete_block† batch_mixed_blocks† |
databases | query_database | create_database update_database delete_database† |
data_sources | list_data_sources get_data_source list_data_source_templates | update_data_source delete_data_source† |
views | list_views get_view query_view | create_view update_view delete_view† |
comments | list_comments get_comment | add_page_comment add_discussion_comment update_comment delete_comment† |
users | list_users get_user get_bot_user get_self | — |
files | list_file_uploads get_file_upload get_file_url get_image | upload_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ência | Nomes | Resolvido por |
|---|---|---|
notion-file:block/<block-id> | O arquivo em um bloco de imagem | get_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.
| env | padrão | significado |
|---|---|---|
MCP_TRANSPORT | stdio | defina para http para habilitar HTTP |
PORT | 3000 | porta de escuta (0 = atribuída pelo SO) |
HOST | 127.0.0.1 | endereç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_HOSTS | localhost + host vinculado | lista separada por vírgulas para lista de permissões Host de rebinding DNS |
MCP_ALLOWED_ORIGINS | origens localhost | lista separada por vírgulas para lista de permissões Origin do navegador |
⚠️ Quem alcançar
/mcpage como seuNOTION_TOKEN. Em loopback, o padrão, isso significa apenas processos locais. Antes de vincular umHOSTnão-loopback, definaMCP_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.
| Área | Operações |
|---|---|
| Páginas | create_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 |
| Blocos | append_blocks, get_block, get_block_children, update_block, delete_block, batch_mixed_blocks |
| Bancos de dados | create_database, query_database, update_database, delete_database |
| Fontes de dados | list_data_sources, get_data_source, update_data_source, delete_data_source, list_data_source_templates |
| Visualizações | list_views, get_view, query_view, create_view, update_view, delete_view |
| Comentários | list_comments, add_page_comment, add_discussion_comment, get_comment, update_comment, delete_comment |
| Usuários | list_users, get_user, get_bot_user, get_self |
| Arquivos | upload_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 recurso | Retorna |
|---|---|
notion://operations | Folha 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_TOKENna configuração do seu cliente, depois se o token ainda está Ativo em app.notion.com/developers/tokens. Instalado comadd-mcpe pulou--env? A entrada não tem token; execute novamente com ele. - "No parent page configured" — passe
parentna chamada, ou definaNOTION_PAGE_ID. multi_source_databasedequery_databaseoucreate_page— o banco de dados tem várias fontes de dados. Chamelist_data_sources, depois passedata_source_id(ou um paidata_source_id) em vez dedatabase_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/node2.0.0); transportes stdio + Streamable HTTP; revisões de protocolo 2024-11-05 até 2026-07-28 (serveStdio/createMcpHandlerpara o caminho sem estado 2026-07-28, o transporte com sessão para o resto) - Notion SDK
@notionhq/client@^5.22.0, fixado emNotion-Version: 2026-03-11 - Validação de payload Zod 4; emite JSON Schema draft-7 com deduplicação
$defspara 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;
withRetrycom 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