Notion

Interactúa con la API de Notion para leer, crear y modificar contenido usando lenguaje natural.

Documentación

Servidor MCP de Notion — Conecta Claude, Cursor y VS Code a Notion

npm version NPM Downloads License Model Context Protocol Stars

Dale a tu IA acceso de lectura/escritura a Notion con un token y un comando. Claude Code, Claude Desktop, Cursor, VS Code, Cline, Zed, cualquier cosa que hable MCP: puede crear páginas, consultar bases de datos, añadir bloques, aplicar plantillas, comentar y subir archivos, en lenguaje natural.

Notion incluye su propio servidor MCP. En esto se diferencia este:

  • Se autentica con un token, así que funciona sin interfaz. El MCP alojado de Notion es solo OAuth y alguien tiene que hacer clic en "Autorizar". Este funciona en CI, cron jobs, agentes en segundo plano e implementaciones autoalojadas.
  • No gasta tu contexto en esquemas de herramientas. El servidor oficial de código abierto carga 24 esquemas de endpoints en el contexto del modelo al conectar: 17,163 tokens, reenviados con cada solicitud durante el resto de la sesión. Este carga tres herramientas, 1,005 tokens — 94% menos, 17× más pequeño — y obtiene el esquema de una operación solo cuando una tarea realmente lo necesita.
  • Tampoco gasta tu contexto en respuestas. Leyendo las mismas páginas a través de ambos servidores, obtener el contenido de una página cuesta 82% menos (26,071 → 4,568 tokens en una página de 88 bloques), una consulta de base de datos de 25 filas 81% menos, un objeto de página 68% menos. El JSON crudo de Notion es mayormente envoltorios id/type, annotations y bloques created_by/parent/icon, y nada de eso llega al modelo. Esa es la mitad que se acumula, porque la superficie de herramientas se paga una vez y las respuestas se pagan en cada llamada. Medido contra un fixture reproducible, con las advertencias indicadas →

Nada se pierde para lograrlo: una consulta de base de datos devuelve filas planas de nombre → valor en lugar de las bolsas crudas properties de Notion (5.3× más ligero en el benchmark), y verbose: true te da la forma intacta del SDK cuando quieras — dentro de 4 tokens de lo que devuelve el servidor oficial, que es como el benchmark demuestra que ambos leen lo mismo. Mutaciones por lotes con rollback atómico, claves de idempotencia, reintentos en límites de tasa y errores de validación autocorregibles están integrados, y la comparación a continuación tiene el resto.

Notion MCP Server on Glama

Inicio rápido

1. Obtén un token de Notion. Abre app.notion.com/developers/tokens → + New token → nómbralo, elige tu espacio de trabajo → Create token → copia el valor de ntn_…. Un Personal Access Token ve todo lo que tú puedes ver, sin compartir por página. (¿Página faltante o vacía? Tu administrador deshabilitó los PAT — consulta alternativas de autenticación.)

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

2. Instálalo.

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

add-mcp encuentra los clientes MCP en tu máquina y escribe la configuración para los que elijas: Claude Code, Claude Desktop, Cursor, VS Code, Codex, Gemini CLI, Cline, Windsurf, Zed y una docena más. Añade -g para instalar a nivel de usuario en lugar del proyecto actual, -a claude-code para omitir el selector, --all para escribir todos los clientes a la vez.

Mantén la bandera --env. Sin ella, la entrada se escribe sin token, y el servidor arranca y luego falla en cada llamada con un error de autenticación.

O instálalo a mano: configuración JSON, Claude Code, Cursor, VS Code, Gemini CLI, Claude Desktop, Docker

Cualquier cliente que lea un bloque mcpServers (el ~/.cursor/mcp.json de Cursor, el claude_desktop_config.json de Claude Desktop, la configuración de 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

Claude Code habla el protocolo de la era 2025 sobre stdio a menos que se le indique lo contrario. Establece MCP_PROTOCOL_NEGOTIATION=auto en su entorno y sondea MCP 2026-07-28 (solicitudes sin estado, sugerencias de caché en cada lista). El servidor atiende ambos.

Cursor: Install MCP Server — haz clic, luego reemplaza YOUR_NOTION_TOKEN en la entrada generada.

VS Code (modo agente de Copilot): Install in VS Code — VS Code te pide el token y lo almacena como entrada secreta.

Gemini CLI:

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

El repositorio incluye un gemini-extension.json, así que esto se instala como extensión: pide el token una vez, lo guarda en tu llavero del sistema e inicia el servidor con npx.

Claude Desktop, sin Node.js: descarga notion-mcp-server.mcpb de la última versión y haz doble clic (o arrástralo a Settings → Extensions), luego pega tu token cuando se te pida. ¿Nunca has editado un archivo de configuración? El tutorial paso a paso no asume 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

La bandera -i es obligatoria para stdio. La imagen cumple con OCI, así que Podman, OrbStack, colima, Rancher Desktop, Finch y nerdctl aceptan las mismas banderas. Para un contenedor HTTP de larga duración, consulta Transporte remoto / HTTP.

3. Pruébalo. En un chat nuevo:

"Usa Notion para crear una página llamada 'Hola desde mi agente' y añade una lista de verificación de tres cosas para probar hoy."

Tu IA llama a notion_write y responde con un enlace a la página en vivo.

Lo que tu IA puede hacer con él

  • "Encuentra cada fila en mi base de datos de Tareas donde Estado sea 'Haciendo' y dime cuáles están vencidas." — filtros tipados where, filas aplanadas
  • "Renombra estas 50 páginas a la nueva convención." — una llamada por lotes, paralelismo de 10 vías, reintento idempotente
  • "Crea una página desde mi plantilla 'Revisión semanal' y completa este resumen."
  • "Reescribe esa página de especificaciones: corrige los encabezados y añade un ejemplo de código." — ida y vuelta de markdown vía get_page_markdown → editar → update_page_markdown
  • "Comenta en las notas de la reunión de ayer con un resumen de un párrafo."
  • "Sube este diagrama a la página de diseño." — subidas de una y varias partes
  • "Mira la captura de pantalla en ese informe de error y dime qué está mal." — get_image entrega la imagen al modelo

El catálogo completo está en el menú de operaciones: 47 operaciones detrás de tres herramientas.

¿Qué MCP de Notion deberías usar?

Mejor paraAutenticaciónSin interfaz / CINotas
MCP alojado de Notion (mcp.notion.com)Chat interactivo en claude.ai, ChatGPT, CursorOAuth (un humano debe hacer clic; Notion dice que la autenticación no interactiva está en desarrollo)❌De primera parte, ~34 herramientas de markdown (11 de ellas herramientas de sesión de Custom Agent que necesitan Notion AI), algunas limitadas por plan
Servidor oficial de código abierto—Token✅Notion lo llama obsoleto y "ya no mantenido activamente"; el repositorio dice que "puede retirarlo" y que los issues y PRs no se monitorean activamente
Este servidorAgentes, automatización, CI, autoalojamiento, cargas sensibles a tokensToken (PAT)✅Mantenido activamente, diseño centrado en agentes

Para chatear con tu Notion en la interfaz web de claude.ai, usa el conector alojado de Notion: es un clic. Usa este servidor cuando el agente funcione sin supervisión, cuando el costo de contexto importe, o cuando quieras semántica por lotes e idempotente y tu propio host.

Comparación detallada vs. el servidor oficial de código abierto
CapacidadNotion MCP oficial (código abierto)Este servidor
Superficie de herramientas24 herramientas (una por endpoint), 17,163 tokens cargados en contexto3 herramientas, 1,005 tokens — 94% menos esquema al conectar
Tamaño de respuestaEnvoltorio completo de Notion en cada lectura82% menos al leer los bloques de una página, 81% en una consulta de base de datos de 25 filas, 68% en un objeto de página, 71% en una búsqueda — mismos objetos, ambos servidores, pares emparejados
Operaciones cubiertas~24 endpoints47 operaciones (más un alias trash_page) en páginas, bloques, bases de datos, fuentes de datos, vistas, plantillas, comentarios, usuarios, archivos
Mutaciones por lotesNo documentado✅ Envoltorio universal { items: [...] }; hasta 10 en paralelo
Lotes atómicos + rollbackNo documentado✅ atomic: true aborta en el primer fallo, archiva con el mejor esfuerzo las entidades creadas antes
IdempotenciaNo documentado✅ idempotency_key — misma clave + operación devuelve el resultado en caché durante 5 minutos
Manejo de límites de tasaLos 429 suben✅ Limitador de cubo de tokens (3 req/s por defecto) + retroceso exponencial, respeta Retry-After
Formas de respuestaJSON crudo del SDK de NotionModeladores esbeltos eliminan ruido por defecto; verbose: true opta por no participar y devuelve la forma cruda
Consultas de base de datosBolsa cruda properties por filaMapa aplanado de nombre → primitivo (todos los 20+ tipos de propiedad) — 16,629 → 3,143 tokens en la consulta de 25 filas del benchmark
Escribir propiedadesJSON de propiedad completo de NotionValores simples: { Status: "Done", Due: "2026-10-01", Tags: ["a"] }, tipados desde el esquema de la fuente de datos (caché de 5 min); nombres y opciones incorrectos rechazados con los válidos
FiltrosJSON de filtro crudo de NotionAbreviatura tipada where — { Status: "Done", Priority: { in: [...] }, OR: [...] } y sorts: ["-Due Date"]; los filtros crudos aún se aceptan
Campos desconocidosRechazadosIgnorados con una entrada warnings que nombra el campo y los aceptados, para que la llamada aún se ejecute
PaginaciónCursores manualespaginate: true opcional recorre next_cursor (límite ≈ 1000 elementos)
Formato de cableSerialización predeterminada del SDKJSON compacto — cargas ~30% más pequeñas
MarkdownHerramientas de markdown a nivel de página✅ Aceptado por create_page / append_blocks / update_block / comentarios, más ida y vuelta completo (get_page_markdown / update_page_markdown), GFM completo
Plantillas—✅ create_page desde una plantilla de Notion + descubrimiento list_data_source_templates
Vistas de base de datos—✅ listar / obtener / consultar / crear / actualizar / eliminar; query_view ejecuta los filtros y ordenamientos almacenados de una vista y devuelve filas hidratadas
Subidas de archivosNo en la superficie de herramientas documentada✅ De una y varias partes (fragmentos de 5 MB), MIME inferido
Errores de validaciónCadena de error simpleAutocorregible: { code, message, path, issues, schema, example, fix } — corregido en un solo ida y vuelta
Versión de la API de Notion—2026-03-11 fijada (fuentes de datos, vistas, plantillas)

Lo que eso te compra en la práctica: renombrar 50 páginas es una llamada notion_write con { items: [...], concurrency: 10 } en lugar de 50 viajes por el bucle de razonamiento del agente, y el ahorro de tokens de prompt es la mitad más grande de la victoria. El benchmark tiene el método, el tokenizador, el control que lo valida, un peor caso honesto y los límites de su propia muestra.

Configuración

Token: PAT o integración interna

Ambos van en la misma variable de entorno NOTION_TOKEN; solo difiere de dónde los obtienes.

Personal Access Token (recomendado)Integración interna (con ámbito)
Dóndeapp.notion.com/developers/tokens → + New tokenapp.notion.com/developers/connections → + New connection
AlcanceTodo lo que tú puedes verSolo páginas donde hiciste clic en • • • → Connect → <integration>
FricciónNingunaUn paso de Connect por página o base de datos
Úsalo cuandoPor defecto: espacios de trabajo personales y de equipo, prototipadoUn administrador requiere ámbito explícito por recurso, o para bots de producción compartidos

💡 La mayoría de los errores object_not_found son la elección de autenticación incorrecta en lugar de un bug: un token de integración interna que nunca se conectó a la página. Cambia a un PAT.

Detalles del PAT: capacidades, caducidad, revocación, alternativa si el administrador lo deshabilita **Puede:** leer todas las páginas a las que tengas acceso; crear y actualizar páginas y bases de datos donde tengas derechos de edición; comentar como tú; subir archivos. **No puede:** acceder a páginas que no puedas ver, eludir permisos del espacio de trabajo, actuar como otro usuario ni cambiar ajustes de administración. El alcance de un PAT es tu cuenta, así que si pierdes acceso a una página, el PAT también lo pierde. Emite tokens separados por cada compañero.

Caducidad: Los PAT caducan 1 año después de su creación (documentación de Notion). Configura un recordatorio para el mes 11.

Revocación: app.notion.com/developers/tokens → Revocar junto al token, con efecto inmediato. Los administradores del espacio de trabajo pueden revocar el de cualquiera desde Ajustes y miembros → Conexiones → Todos los tokens de acceso personal.

¿PAT deshabilitados por el administrador? Pídeles que lo habiliten, o crea una Integración Interna en app.notion.com/developers/connections (+ Nueva conexión) y con • • • → Conectar vincúlala a cada página que el agente deba tocar. Misma variable de entorno NOTION_TOKEN.

Referencia oficial: Guía de PAT · Resumen de autorización.

Variables de entorno

Variable de entornoObligatoriaPredeterminadoSignificado
NOTION_TOKEN✅—PAT (ntn_…, recomendado) o secreto de Integración Interna (secret_… / ntn_…)
NOTION_PAGE_ID——Padre predeterminado para create_page / create_database cuando no se pasa ningún parent (página → Compartir → Copiar enlace; sirve la URL completa o el id de 32 caracteres)
NOTION_RATE_LIMIT—3Solicitudes/segundo para el limitador compartido (límite documentado por integración de Notion)
NOTION_READ_ONLY——true/1/yes desactiva toda operación de escritura con un solo interruptor
NOTION_ALLOWED_OPERATIONS—allLista de permitidos separada por comas de operaciones o ajustes predefinidos de grupo — ver Restringir operaciones
NOTION_BLOCKED_OPERATIONS——Lista de bloqueados separada por comas (mismo vocabulario); prevalece sobre la lista de permitidos
NOTION_CONFIRM_DESTRUCTIVE——true/1 mantiene las operaciones destructivas habilitadas pero te pregunta primero — ver Restringir operaciones
NOTION_UPLOAD_ROOT——Confina la fuente de path de upload_file a un solo directorio — ver Archivos
NOTION_FILE_URLS—fullref reemplaza las URL firmadas de archivos de Notion (~1.650 caracteres, válidas por una hora) en respuestas reducidas con referencias notion-file: cortas — ver Archivos
HTTPS_PROXY / HTTP_PROXY——Enruta todo el tráfico saliente — llamadas a la API de Notion y las descargas en la fuente url de get_image y upload_file — a través de un proxy HTTP(S) (variables de entorno estándar, también se aceptan minúsculas)
NOTION_DAILY_LOG_PAGE_ID——Solo lo usa el prompt MCP de registro diario

Las variables de transporte HTTP (MCP_TRANSPORT, PORT, HOST, MCP_AUTH_TOKEN, …) están en Transporte remoto / HTTP.

¿Actualizando desde v1.x o v2.x? Cada variable de entorno sigue funcionando sin cambios. La diferencia está en la superficie de herramientas: las cinco herramientas de v1, luego las notion_execute de v2, se convirtieron en notion_read + notion_write, y notion_describe quedó como estaba. Los clientes modernos redescubren las herramientas automáticamente. Detalles en MIGRATION.md.

Restringir operaciones

NOTION_ALLOWED_OPERATIONS (lista de permitidos) y NOTION_BLOCKED_OPERATIONS (lista de bloqueados) aceptan cada una una lista separada por comas de ajustes predefinidos de grupo o nombres exactos de operación.

Ajuste predefinidoSe expande a
readtoda operación no mutante
writetoda operación mutante
destructiveoperaciones cuyo propósito es la eliminación (archive_page/trash_page, delete_block, batch_mixed_blocks, delete_comment, delete_view)
pages blocks databases data_sources views comments users filestoda operación de esa familia, de lectura y escritura
{ "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" } }

Los nombres no distinguen mayúsculas de minúsculas, los tokens desconocidos se ignoran con una advertencia, la lista de bloqueados prevalece, y una lista de permitidos que se resuelva a cero operaciones lo desactiva todo (cierre seguro). Las operaciones deshabilitadas desaparecen de los enums operation de las herramientas, de notion_describe y del menú notion://operations, así que nombrar una falla la validación antes de que se ejecute; cuando no hay ninguna operación de escritura habilitada, notion_write no se anuncia en absoluto. Una línea en stderr al inicio dice qué se resolvió. Revísala primero cuando la configuración no se comporte como esperas:

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

Confirmar en lugar de bloquear. NOTION_CONFIRM_DESTRUCTIVE=true mantiene disponibles las operaciones destructivas y hace que notion_write te pregunte antes de ejecutar una, mediante elicitation de MCP: una solicitud elicitation/create en clientes de la era 2025, un viaje de ida y vuelta input_required en clientes MCP 2026-07-28, donde el reintento lleva un requestState sellado que solo coincide con la llamada para la que fue emitido. Recibes un diálogo de sí/no que nombra la operación y su objetivo (la página, base de datos, fuente de datos o título de bloque cuando una recuperación puede obtenerlo en 5 s; si no, el id; para un lote, cuántos elementos).

Las restauraciones (restore_page, delete_database / delete_data_source con in_trash: false) y una llamada batch_mixed_blocks sin entrada delete nunca preguntan, y una operación bloqueada se rechaza igualmente con operation_not_allowed antes de preguntar a nadie. Rechazar, cancelar o responder no hace que la llamada devuelva confirmation_declined; las instrucciones del servidor le dicen al modelo que no reintente y que te pregunte a ti en su lugar. Un cliente que no haya declarado la capacidad de elicitation recibe confirmation_unavailable en lugar de una ejecución silenciosa. Usa un cliente que admita elicitation, desactiva la variable o bloquea las operaciones destructivas por completo.

Referencia por operación y limitaciones
DominioLecturaEscritura
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

† = también en el grupo destructive.

Limitaciones. El control es por operación, no por parámetro: update_page_markdown es una operación de escritura que puede reemplazar el cuerpo de una página, y bloquear destructive no la desactiva. Para un despliegue garantizado sin mutaciones usa NOTION_ALLOWED_OPERATIONS=read o NOTION_READ_ONLY=true. Los prompts de MCP pueden seguir mencionando operaciones deshabilitadas, pero la ejecución se rechaza.

Archivos

Subidas. upload_file toma sus bytes como base64, un url público, o un path local que el servidor lee directamente. Una fuente path puede leer cualquier archivo que el proceso del servidor pueda, así que cuando un modelo componga la ruta, configura NOTION_UPLOAD_ROOT para confinarla: las rutas relativas se resuelven dentro de la raíz, y los enlaces simbólicos se resuelven antes de la verificación para que no puedan apuntar fuera de ella.

URL de archivos. Notion emite una URL S3 firmada nueva para cada archivo alojado en cada lectura: unos 1.650 caracteres (~500 tokens), válida por una hora, diferente cada vez, y fácil de estropear para un modelo pequeño. NOTION_FILE_URLS=ref las reemplaza en respuestas reducidas (get_page, search_pages, query_database, query_view, get_block, get_block_children, …) con referencias cortas y estables.

ReferenciaNombresResuelta por
notion-file:block/<block-id>El archivo en un bloque de imagenget_file_url → { ref, url }, una URL firmada nueva válida por aproximadamente una hora
notion-file:page/<page-id>/<property>/<index>Una entrada de la propiedad files de una página (nombre de propiedad codificado en URL)get_image → la imagen como contenido de imagen MCP, para que el modelo pueda verla (solo image/*, hasta 5 MB)

Ambos resolvedores releen el objeto a través de la API de Notion, así que una referencia sigue siendo válida mientras el archivo exista. get_image obtiene solo la URL que Notion devolvió para un archivo alojado en Notion, nunca una proporcionada por quien llama, así que no se puede dirigir a un host de LAN, un endpoint de metadatos en la nube o un objetivo de exfiltración. Las URL externas (imágenes enlazadas, archivos external) ya son cortas y estables: pasan sin cambios en cualquier modo, y get_image las devuelve como texto en lugar de obtenerlas. get_page_markdown es el markdown renderizado propio de Notion y no se reescribe. El valor predeterminado, full, deja cada respuesta como estaba.

Transporte remoto / HTTP

El servidor habla stdio por defecto. Configura MCP_TRANSPORT=http para ejecutarlo como endpoint remoto en su lugar, para clientes web, agentes en red y despliegues compartidos:

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

Sirve MCP Streamable HTTP en /mcp para ambas generaciones de protocolo actuales, elegido por solicitud según lo que envíe el cliente. Los clientes MCP 2026-07-28 obtienen la ruta sin estado, donde cada POST es independiente: sin sesión, server/discover, sugerencias de caché en cada lista. Los clientes 2024-11-05 … 2025-11-25 obtienen sesiones mediante el encabezado mcp-session-id más el flujo GET y DELETE, y un GET/DELETE sin id de sesión recibe 405. También hay un GET /health sin autenticación. El proceso es de un solo inquilino: cada solicitud actúa como el único NOTION_TOKEN con el que comenzó.

envpredeterminadosignificado
MCP_TRANSPORTstdioconfigúralo en http para habilitar HTTP
PORT3000puerto de escucha (0 = asignado por el SO)
HOST127.0.0.1dirección de enlace; configura 0.0.0.0 para exponer externamente (solo con MCP_AUTH_TOKEN)
MCP_AUTH_TOKEN—cuando se configura, cada solicitud /mcp debe enviar Authorization: Bearer <token>
MCP_ALLOWED_HOSTSlocalhost + host enlazadolista separada por comas para la lista de permitidos Host de reenlace de DNS
MCP_ALLOWED_ORIGINSorígenes de localhostlista separada por comas para la lista de permitidos Origin del navegador

⚠️ Quien llegue a /mcp actúa como tu NOTION_TOKEN. En loopback, el valor predeterminado, eso significa solo procesos locales. Antes de enlazar un HOST que no sea loopback, configura MCP_AUTH_TOKEN (el servidor advierte si no lo haces) y pon un proxy inverso autenticador delante.

Conexión desde un cliente que admita encabezados (Claude Code, Cursor, VS Code) y verificación local:

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

En Docker, HOST=0.0.0.0 es lo que hace alcanzable el puerto publicado, ya que dentro del contenedor 127.0.0.1 es el loopback propio del contenedor. Un enlace que no sea loopback es exactamente donde MCP_AUTH_TOKEN demuestra su 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

Las compilaciones de Claude Desktop afectadas por anthropics/claude-code#93290 envían un cuerpo 2026-07-28 bajo un encabezado MCP-Protocol-Version: 2025-11-25. El servidor realinea esa única discrepancia conocida para que esas compilaciones funcionen; cualquier otro desajuste entre encabezado y cuerpo recibe el rechazo que prescribe la especificación (-32020).

Comprobaciones de salud para un contenedor HTTP La imagen se distribuye sin un `HEALTHCHECK` porque arranca en modo stdio, donde nada escucha y una sonda integrada de `/health` marcaría cada contenedor stdio como no saludable. Añade uno tú mismo para un despliegue HTTP. El mismo comando está comentado en el `Dockerfile` y funciona como `--health-cmd` en `docker run` también:
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

Herramientas MCP

Tres herramientas, cualquiera de las 47 operaciones que termines llamando. notion_read ejecuta las lecturas, notion_write las escrituras y notion_describe devuelve el JSON Schema de una operación más un ejemplo funcional, lo que vale un viaje de ida y vuelta antes de una llamada compleja: expresiones de filtro, lotes de bloques mixtos, definiciones de propiedades de base de datos. El campo operation de cada herramienta es un enum de exactamente lo que este servidor tiene habilitado, así que el menú se envía con la lista de herramientas, un cliente puede validar una llamada antes de enviarla, y un nombre enviado a la herramienta equivocada falla en un viaje de ida y vuelta con un mensaje que nombra la correcta.

Cada campo de id (page_id, block_id, database_id, view_id, …) también acepta una URL de Notion, así que pega lo que Compartir → Copiar enlace te da. El #fragment de un enlace de bloque se usa para campos block_id y el ?v= de un enlace de base de datos 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" }
  }
}

Una carga útil que no valida regresa con el JSON Schema completo de la operación, un ejemplo funcional y una pista fix, para que la próxima llamada pueda corregirse sin un viaje de ida y vuelta notion_describe.

Permisos por herramienta

Los clientes MCP otorgan permisos por nombre de herramienta, así que la división lectura/escritura te permite aprobar lecturas una vez y mantener las escrituras detrás de un aviso. En Claude Code (~/.claude/settings.json o el .claude/settings.json del proyecto, donde notion es como sea que hayas nombrado el servidor):

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

La configuración MCP de Cursor ofrece la misma lista de permitidos por herramienta. notion_read está anotado como readOnlyHint: true y notion_write como destructiveHint: true, para clientes que leen anotaciones.

Menú de operaciones (47 operaciones, más un alias)

Las lecturas (get_*, list_*, search_pages, query_database, query_view) pasan por notion_read, todo lo demás por notion_write.

ÁreaOperaciones
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
Bloquesappend_blocks, get_block, get_block_children, update_block, delete_block, batch_mixed_blocks
Bases de datoscreate_database, query_database, update_database, delete_database
Fuentes de datoslist_data_sources, get_data_source, update_data_source, delete_data_source, list_data_source_templates
Vistaslist_views, get_view, query_view, create_view, update_view, delete_view
Comentarioslist_comments, add_page_comment, add_discussion_comment, get_comment, update_comment, delete_comment
Usuarioslist_users, get_user, get_bot_user, get_self
Archivosupload_file, list_file_uploads, get_file_upload, get_file_url, get_image

La lista autoritativa, con capacidad de lote y la herramienta que ejecuta cada operación, se sirve como recurso MCP en notion://operations.

Recursos MCP

Los clientes que admiten adjuntar recursos (mención @) pueden traer contenido de Notion al contexto sin una llamada de herramienta. Los recursos dinámicos pasan por la misma autenticación, límite de velocidad y control de acceso que las llamadas de herramienta.

URI del recursoDevuelve
notion://operationsHoja de referencia Markdown de cada operación habilitada
notion://page/<page_id>Cuerpo de la página como markdown
notion://database/<data_source_id>Esquema de la fuente de datos como JSON

Solución de problemas

  • object_not_found / "No se pudo encontrar …" — un token de Integración Interna solo ve páginas explícitamente Conectadas a él. Cambia a un PAT para omitir el uso compartido por página.
  • "Falló la autenticación de Notion" en cada llamada — token faltante, revocado o caducado (los PAT duran un año). Verifica NOTION_TOKEN en la configuración de tu cliente, luego que el token siga Activo en app.notion.com/developers/tokens. ¿Instalado con add-mcp y omitido --env? La entrada no tiene token; vuelve a ejecutarlo con él.
  • "No se configuró una página principal" — pasa parent en la llamada, o establece NOTION_PAGE_ID.
  • multi_source_database de query_database o create_page — la base de datos tiene varias fuentes de datos. Llama a list_data_sources, luego pasa data_source_id (o un padre data_source_id) en lugar de database_id.
  • Un resultado exitoso lleva warnings — la llamada se ejecutó; cada entrada nombra un campo que se ignoró (mal escrito o mal colocado) o un nombre de propiedad que se corrigió. Arregla la carga útil la próxima vez, nada que reintentar.
  • Las herramientas no aparecen en Claude Desktop — error tipográfico en el token (debe permanecer dentro de las comillas) o la aplicación no se cerró por completo (Cmd+Q, no cerrar la ventana) antes de reabrirla.
  • Los registros de inicio dicen "Falló la verificación de autenticación de Notion" pero las herramientas funcionan — la verificación de inicio es de mejor esfuerzo; ignórala si las llamadas tienen éxito.
  • Docker sale inmediatamente / "Conexión cerrada" — el indicador -i es obligatorio: docker run --rm -i ….
  • Docker: "NOTION_TOKEN no está establecido" a pesar de -e — escribe -e NOTION_TOKEN (reenvía desde el entorno padre) o -e NOTION_TOKEN=ntn_xxx, no -e NOTION_TOKEN ntn_xxx.

¿Sigue atascado? Problemas de GitHub · Preguntas frecuentes · Referencia de la API de Notion · Especificación MCP

Privacidad

El servidor se ejecuta en tu máquina o en tu propio host y solo habla con api.notion.com, a través de HTTPS, con el token que configures. Sin telemetría, sin análisis, sin servidor nuestro en el camino: nada de lo que leas o escribas en Notion va a ningún otro lugar. El token permanece donde tu cliente MCP lo guarda, en su archivo de configuración o en un llavero para clientes que tengan uno. Con HTTPS_PROXY establecido, el tráfico pasa por tu proxy en su lugar. get_image obtiene solo las URL firmadas que Notion devuelve para los archivos que aloja, nunca una URL suministrada por el modelo, y upload_file lee un archivo local solo cuando se le pide, dentro de NOTION_UPLOAD_ROOT cuando eso está establecido. El manejo de tus datos por parte de Notion está cubierto por la política de privacidad de Notion.

Desarrollo

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

Apunta un cliente a la compilación local en lugar de npx:

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

Los registros van a stderr y también se envían al cliente como entradas MCP notifications/message (registrador notion-mcp-server), para que aparezcan en la vista de registros del propio cliente — el canal de salida de VS Code, MCP Inspector, los registros de Claude Desktop — donde stderr suele estar oculto. Los clientes de la era 2025 eligen el nivel con logging/setLevel (por defecto info); los clientes MCP 2026-07-28 no tienen tal llamada y preguntan por solicitud con la clave de sobre io.modelcontextprotocol/logLevel, así que una solicitud sin ella no recibe notificaciones de registro. Stderr no se ve afectado de ninguna manera. En debug también obtienes una línea por llamada notion_read / notion_write: operación, tamaño del lote, duración, ok o error, nunca la carga útil o el contenido de la página.

Detalles técnicos: cómo está construido
  • TypeScript + SDK de TypeScript MCP v2 (@modelcontextprotocol/server + @modelcontextprotocol/node 2.0.0); transportes stdio + Streamable HTTP; revisiones de protocolo 2024-11-05 a 2026-07-28 (serveStdio / createMcpHandler para la ruta sin estado 2026-07-28, el transporte con sesión para el resto)
  • SDK de Notion @notionhq/client@^5.22.0, fijado en Notion-Version: 2026-03-11
  • Validación de carga útil Zod 4; emite JSON Schema draft-7 con deduplicación $defs para sobres de error
  • Markdown → bloques de Notion vía remark / remark-gfm
  • Trabajador de lotes con concurrencia limitada (por defecto 3, máximo 10); limitador de velocidad de cubo de fichas compartido; withRetry con retroceso exponencial alrededor de cada llamada enviada
  • Caché de idempotencia en memoria (TTL de 5 minutos, 512 entradas)
  • Modeladores delgados por tipo de entidad con exclusión voluntaria verbose: true
  • Suite Vitest que cubre el analizador de markdown, modeladores, emisor de esquema, despachador, semántica de lotes (éxito parcial / reversión atómica / idempotencia), control de acceso y transporte HTTP
Prueba de humo de extremo a extremo contra un espacio de trabajo real

npm test se ejecuta contra un cliente de Notion simulado. scripts/e2e.mjs impulsa el servidor compilado a través de stdio contra un espacio de trabajo real: cada operación de lectura, los recursos y avisos, notion_describe para cada operación, y, con --write, cada operación de escritura dentro de una página desechable.

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)

Imprime una tabla de APROBADO/FALLIDO por verificación, lista cualquier operación que la ejecución no alcanzó y sale con código distinto de cero en caso de fallo. No es parte de CI.

Contribuciones

Se aceptan solicitudes de extracción. Bifurca → rama → confirma → empuja → PR. Ejecuta npm test antes de enviar.

Licencia

MIT — ver LICENCIA.


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