Fresh Jots MCP Server

Servidor MCP de FreshJotsDotCom

Documentación

REST con token Bearer. Solo notas de texto plano. Disponible en los planes Dev y Team. Para la propuesta sistémica — cada script tiene su propio cuaderno, anexar por nombre de archivo para trabajos cron y resúmenes de sesiones de IA, flujos de trabajo del mundo real — consulta /for/developers. URL base: https://freshjots.com/api/v1. Legible por máquinas: la misma API como documento OpenAPI 3.1 en /openapi.json, para generadores de clientes, Postman y herramientas de agentes.

Referencia rápida — clientes

Linux y macOS

homebrew-freshjots

Windows y JS

freshjots-js

Gema Ruby

gem "freshjots"

Ejemplos rápidos de uso de la API. Ejemplos listos para copiar de curl para los flujos de trabajo comunes: crear, registros de solo anexar, anexar por nombre de archivo, listar.

Ejemplos rápidos →

¿Trabajas con carpetas? Crea carpetas, coloca notas en ellas al crearlas, sube en lote a una carpeta.

API de carpetas →

Heartbeat para programadores. Una URL de ping por fuente, verificada cada minuto: cron, systemd, Docker, Kubernetes, Ruby, Python, Node, Go, PowerShell y el informe JSON. Guías para Docker, tu propio código, trabajadores de trabajos y máquinas detrás de NAT.

Ejemplos de heartbeat →

¿En Windows o prefieres no instalar? Escribe a tu cuenta desde PowerShell sin instalación, o con el cliente npm / pip — no se necesita WSL.

Windows / PowerShell →

Autenticación

Genera un token personal en /settings/api_tokens. Los tokens se muestran una sola vez al crearlos y se almacenan como un hash de un solo sentido — cópialo a tu gestor de contraseñas o perfil de shell de inmediato.

Authorization: Bearer mn_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Un token faltante o inválido devuelve 401 unauthenticated. El titular del token sin acceso a la API (plan Free / Personal) devuelve 403 forbidden.

Ámbitos. Cada token se crea con un nivel de permiso — read_write (acceso completo, el predeterminado), read_only (solo GET / HEAD) o write_only (solo POST / PATCH / PUT / DELETE). Un token también puede estar bloqueado a una sola nota, de modo que pueda leer o anexar exactamente a ese flujo y nada más — útil como credencial por script. Un cuarto ámbito, append, respalda la extensión del navegador: creado en Configuración → Extensión del navegador (abierto a todo suscriptor activo, incluido Personal), es solo POST y siempre está bloqueado a una nota, de modo que la extensión puede recortar páginas en esa nota y nada más. Una solicitud que quede fuera del ámbito del token devuelve 403 forbidden con un mensaje que nombra la restricción. Elige el ámbito al crear el token.

Tokens de equipo

Los espacios de trabajo en el plan Team crean tokens en /team/api_tokens (solo propietario / administrador). Mismo formato de cable de token Bearer, mismos endpoints, mismo envoltorio de errores — pero cada lectura y escritura se resuelve a las notas, carpetas y grupo de almacenamiento del equipo en lugar del grupo personal del actor.

  • GET /notes lista solo las notas del equipo; las notas personales del actor nunca aparecen.
  • POST /notes crea con team_id establecido; el user_id de la fila registra al actor (quién lo escribió) para el registro de auditoría en /team/audit_events.
  • /folders/*, /notes/bulk y /notes/:id/move son conscientes del ámbito — un token de equipo solo ve las carpetas del equipo, nunca las personales, y rechaza la colocación entre grupos con 404 not_found.
  • Almacenamiento, límites de velocidad y topes por plan provienen de la suscripción del equipo, no del actor. El lote está habilitado en cada token de equipo — la suscripción del equipo es el derecho.

El tope de tokens activos de un equipo es de 30 tokens activos a la vez. La revocación vive en el mismo panel — los tokens revocados permanecen listados para auditoría pero dejan de autenticar de inmediato.

Endpoints

MétodoRutaPropósito
GET/notesListar notas (resumen). Buscar ?q=<words> (la misma búsqueda de texto completo que la aplicación: título, nombre de archivo y cuerpo, mejor coincidencia primero a menos que se dé ?sort). Filtrar ?format=plain|rich, ?folder_id=N (o none para sin carpeta). Ordenar ?sort=created|updated|appended (predeterminado actualizado). Paginar ?limit=N&offset=N (máx. 200/página).
GET/notes/:idNota completa (plain_body + byte_size). Para seguir un registro: ?tail=50 devuelve las 50 líneas más recientes, ?since=<bytes> solo lo anexado después de que el cuerpo tuviera esa longitud (almacena el byte_size devuelto como el siguiente since); slice en la respuesta describe el corte.
POST/notesCrear nota de texto plano. Cuerpo: {note: {title, plain_body, folder_id?, append_only?, client_encrypted?}}. client_encrypted: true almacena una nota que cifraste localmente con tu propia clave — se conserva tal cual, nunca se lee ni indexa (solo cuentas personales; inmutable tras la creación).
PATCH/notes/:idActualizar título / plain_body / configuración. La configuración incluye append_deadline_hours, alert_email, match_pattern y los campos de webhook. El formato es inmutable. En notas de solo anexar, los campos de contenido se rechazan pero la configuración (folder_id, append_deadline_hours, alert_email, webhook_url, webhook_secret) se acepta.
DELETE/notes/:idEliminar nota.
POST/notes/:id/appendAnexo atómico a plain_body. Cuerpo: {text}.
POST/notes/:id/moveMover a carpeta. Cuerpo: {folder_id} o null.
GET/notes/by-filename/:filenameBuscar por nombre de archivo en lugar de id. Acepta los mismos ?tail y ?since que GET /notes/:id.
PATCH/notes/by-filename/:filenameMisma forma de cuerpo que PATCH /notes/:id, dirigida por nombre de flujo. Útil para reconfigurar la nota de un script (plazo, URL de webhook) sin buscar primero el id.
POST/notes/by-filename/:filename/appendDireccionamiento de flujo — anexar por nombre de archivo. Crea la nota si falta (solo anexar por defecto). En el primer toque, client_encrypted: true la abre como un flujo cifrado por el cliente — envía una línea de texto cifrado autocontenida por anexo (solo cuentas personales). Se ignora una vez que la nota existe.
POST/notes/bulkHasta 50 creaciones por llamada. Cuerpo: {notes: [...]}.
GET/foldersListar carpetas.
GET/folders/:idMostrar una sola carpeta.
POST/foldersCrear carpeta. Cuerpo: {folder: {name}}.
PATCH/folders/:idRenombrar carpeta.
DELETE/folders/:idEliminar carpeta. Las notas dentro se conservan (movidas a la carpeta integrada default).

Límites de velocidad

  • Dev: 600 lecturas / 60 escrituras / 300 anexos por minuto, por token. 3 tokens activos. 15 GB de almacenamiento. Endpoint de lote habilitado.
  • Team: 2,000 lecturas / 200 escrituras / 1,000 anexos por minuto, por token. 30 tokens activos por equipo. 50 GB de almacenamiento del espacio de trabajo. Endpoint de lote habilitado. 50,000 notas de texto plano / 1,000 notas enriquecidas en todo el equipo.

Las solicitudes limitadas devuelven 429 rate_limited con los encabezados Retry-After, X-RateLimit-Limit, X-RateLimit-Remaining y X-RateLimit-Reset.

El desglose completo por plan — incluidos los topes de tamaño por nota, los límites de la superficie del navegador, los techos de paginación, la política de expiración de tokens y la ventana de exportación — está en la página Límites del servicio.

Reintentos idempotentes

Cada endpoint de escritura (POST, PATCH, PUT, DELETE) acepta un encabezado de solicitud Idempotency-Key. Una solicitud repetida con la misma clave + la misma huella del cuerpo reproduce la respuesta original en lugar de ejecutar la acción dos veces — seguro para reintentos de "¿la solicitud anterior tuvo éxito antes de mi tiempo de espera?" desde trabajos cron y scripts de CI.

  • Encabezado: Idempotency-Key: <your-key>
  • Formato de clave: 8..255 caracteres. Los UUID funcionan; cualquier cadena en ese rango también.
  • Ventana de reproducción: 24 horas. Después, la misma clave se acepta como una solicitud nueva.
  • Las reproducciones llevan Idempotent-Replay: true en la respuesta para que tu cliente pueda distinguirlo.

Una clave repetida con una huella del cuerpo diferente devuelve 409 idempotency_key_conflict — el servidor se niega a sobrescribir silenciosamente la respuesta de una solicitud anterior. O reintenta con el cuerpo original o genera una clave nueva.

curl -X POST https://freshjots.com/api/v1/notes \
  -H "Authorization: Bearer $FRESH_JOTS_TOKEN" \
  -H "Idempotency-Key: cron-2026-05-06-evening-digest" \
  -H "Content-Type: application/json" \
  -d '{"note":{"title":"Evening digest","plain_body":"...","format":"plain"}}'

Envoltorio de errores

Todas las respuestas de error comparten la misma forma:

{ "error": { "code": "validation_failed", "message": "Title can't be blank", "details": ["Title can't be blank"] } }

Códigos de error estables:

  • unauthenticated — token faltante / inválido (401)
  • forbidden — el token carece de acceso a la API, está fuera de ámbito o la cuenta no está confirmada (403)
  • note_locked — intento de edición de contenido en una nota de solo anexar; los anexos y la eliminación están permitidos, las ediciones no (403)
  • not_found — registro ausente o propiedad de otro usuario (404)
  • validation_failed — entrada incorrecta o violación de esquema (422)
  • cap_exceeded — recuento de notas por encima del tope de tu plan (422)
  • storage_cap_exceeded — los bytes totales excederían tu tope de almacenamiento (422)
  • content_too_large — una sola nota por encima del tope de bytes por formato (413). Una nota de solo anexar se desplaza en lugar de rechazar (consulta notas de solo anexar), así que en una de estas significa que el texto solo excede el tope o que el desplazamiento fue bloqueado; el mensaje indica cuál.
  • content_type_mismatch — intento de escritura de nota enriquecida a través de la API (422)
  • rate_limited — ventana de limitación excedida (429)
  • idempotency_key_conflict — clave de reproducción reutilizada con un cuerpo diferente (409). Consulta Reintentos idempotentes arriba.
  • service_unavailable — backend temporalmente inalcanzable; seguro reintentar con retroceso (503)

Watchdog y webhooks

Dos perillas por nota para convertir un flujo en un sustrato de monitoreo. Ambas se configuran mediante PATCH /notes/:id (o PATCH /notes/by-filename/:filename) y también se pueden establecer desde la página de Configuración por nota en la interfaz web. Ambas requieren que la nota sea de solo anexar; el indicador subyacente se establece en la creación ({"append_only": true}) o mediante el interruptor de bloqueo del lado del navegador.

Alertas de interruptor de hombre muerto

Establece append_deadline_hours en una nota de solo anexar (1–720) y Fresh Jots te envía un correo cuando no llega ningún anexo en esa ventana. El siguiente anexo limpia la alerta; si el script se recupera y luego falla de nuevo, recibes un correo nuevo — sin reinicio manual. El alert_email opcional anula el destino (por defecto es el correo de tu cuenta).

curl -X PATCH https://freshjots.com/api/v1/notes/by-filename/cron-jobs-prod \
  -H "Authorization: Bearer $FRESHJOTS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"note":{"append_deadline_hours":2,"alert_email":"oncall@example.com"}}'

Alertas de coincidencia de contenido

El interruptor de hombre muerto informa silencio; una regla de coincidencia de contenido informa una línea. Establece match_pattern en una nota de solo anexar (texto sin distinción de mayúsculas, hasta 200 caracteres) y Fresh Jots te envía un correo cuando un anexo lo contiene, con la línea coincidente en el correo. Una ráfaga de líneas coincidentes envía una alerta, luego como máximo una más cada 10 minutos mientras sigan llegando; la nota en sí tiene cada línea. El destino es el mismo que el del interruptor de hombre muerto: alert_email o el correo de tu cuenta para una nota personal, el destino de alerta del espacio de trabajo para una nota de equipo (un evento team_alert.content_match firmado cuando ese destino es un webhook). El campo de respuesta match_alerted_at muestra la última alerta.

curl -X PATCH https://freshjots.com/api/v1/notes/by-filename/cron-jobs-prod \
  -H "Authorization: Bearer $FRESHJOTS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"note":{"match_pattern":"Traceback"}}'

Webhooks salientes

¿Una URL para todo? Establece un webhook de alerta predeterminado en Configuración: una nota o ranura de heartbeat sin URL propia publica allí (solo notas personales; las notas de equipo usan el destino del espacio de trabajo). Establece webhook_url en una nota de solo anexar y cada anexo exitoso publica el contenido nuevo en tu endpoint. Por defecto, el cuerpo es un envoltorio JSON firmado — HMAC-SHA256 en el encabezado X-FreshJots-Signature (formato: sha256=<hex>) usando el webhook_secret que configures; un secreto en blanco firma con la cadena vacía, y el secreto nunca se lee de vuelta a través de la API. Establece webhook_format a un formato de chat — slack, discord, google_chat, mattermost o rocket_chat — para enviar un mensaje de chat nativo en su lugar (consulta "Formatos de carga útil" abajo).

Diez respuestas consecutivas no-2xx (o fallos de transporte) deshabilitan automáticamente el webhook para que un receptor muerto no drene la cola de trabajos. Vuelve a armarlo guardando la nota de nuevo — ya sea con una URL nueva o, una vez deshabilitado automáticamente, con la URL sin cambios (el guardado deliberado es el acuse de recibo). webhook_failure_count y webhook_disabled_at se exponen en GET /notes/:id para monitoreo.

curl -X PATCH https://freshjots.com/api/v1/notes/by-filename/payments-prod \
  -H "Authorization: Bearer $FRESHJOTS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"note":{"webhook_url":"https://hooks.example.com/x","webhook_secret":"sk_..."}}'

Forma de la carga útil:

{
  "event": "note.appended",
  "delivered_at": "2026-05-02T12:34:56Z",
  "delivery_id": "<uuid>",
  "note": {
    "id": 123, "filename": "payments-prod", "title": "payments-prod",
    "byte_size": 4096, "last_appended_at": "2026-05-02T12:34:55Z"
  },
  "appended_text": "...new chunk (capped at 8 KB)...",
  "appended_bytes": 42,
  "appended_truncated": false
}

Formatos de carga útil

webhook_format controla la forma del payload y acepta seis valores. generic (predeterminado: el sobre firmado note.appended que se muestra arriba, para tu propio servidor o un centro de automatización como Zapier / Make / n8n) es el único formato firmado. Los otros cinco son mensajes de chat nativos: pega la URL del webhook entrante de esa plataforma y listo, sin necesidad de escribir un adaptador: slack (https://hooks.slack.com/services/...), discord (https://discord.com/api/webhooks/...), google_chat (https://chat.googleapis.com/v1/spaces/...), mattermost (una URL de <your-host>/hooks/<token>) y rocket_chat (una URL de <your-host>/hooks/<id>/<token>). Configúralo desde el menú desplegable de la página de Configuración o desde el mismo endpoint PATCH que acepta webhook_url / webhook_secret; el valor actual se devuelve en cada GET /notes/:id. Una nota sin configurar usa por defecto generic.

Los mensajes en formato de chat se recortan para ajustarse a cada plataforma: Slack a 3500 caracteres, Discord a 1900 (por debajo del límite máximo de 2000 caracteres de Discord en el campo content), y Google Chat, Mattermost y Rocket.Chat a 4000 cada uno. La vista previa genérica de appended_text no cambia (limitada a 8 KB; el cuerpo completo siempre es accesible a través de GET /notes/:id).

Las entregas en formato de chat no están firmadas. Ninguno de los webhooks entrantes de las plataformas de chat puede verificar una firma, por lo que el encabezado X-FreshJots-Signature se omite por completo para los formatos slack, discord, google_chat, mattermost y rocket_chat. Para esos, la URL del hook imposible de adivinar es la credencial; trátala como una contraseña. Las reglas de firma en "Verificación de entregas" a continuación se aplican solo al formato Genérico.

curl -X PATCH https://freshjots.com/api/v1/notes/by-filename/cron-jobs-prod \
  -H "Authorization: Bearer $FRESHJOTS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"note":{"webhook_url":"https://hooks.slack.com/services/T.../B.../...","webhook_format":"slack"}}'

Verificación de entregas (solo Genérico)

Tu endpoint es una URL pública, así que verifica cada entrega antes de confiar en ella. Configura el mismo webhook_secret en tu servidor receptor, recalcula el HMAC sobre el cuerpo de la solicitud crudo, sin analizar, y compáralo con el encabezado X-FreshJots-Signature usando una comparación de tiempo constante. Rechaza si hay discrepancia antes de analizar el JSON. (Si configuraste un secreto en blanco, la clave es la cadena vacía).

require "openssl"
require "active_support/security_utils"

SECRET = ENV.fetch("FRESHJOTS_WEBHOOK_SECRET")  # the same value you set on the note

def verified?(raw_body, header)
  expected = "sha256=#{OpenSSL::HMAC.hexdigest("SHA256", SECRET, raw_body)}"
  header.to_s.bytesize == expected.bytesize &&
    ActiveSupport::SecurityUtils.secure_compare(header.to_s, expected)
end

# Rails: verify request.raw_post (NOT params) against
# request.headers["X-FreshJots-Signature"], then head(:unauthorized) on false.

Actualizaciones en vivo del navegador

Aviso: cuando tienes una nota abierta en la interfaz web y una escritura de API la modifica, solo las notas de solo añadir se actualizan en su lugar: el navegador se suscribe a un flujo por usuario y reemplaza el cuerpo a medida que llega nuevo contenido, sin necesidad de actualizar. Pruébalo: abre una nota de solo añadir en una pestaña y luego curl una adición desde otra ventana.

Notas editables (CRUD) actualizadas a través de la API (PATCH /api/v1/notes/:id, POST /notes/:id/append en una nota no bloqueada) no se envían a una pestaña del navegador abierta: actualiza la página para ver el nuevo contenido. Esto es por diseño: enviar cambios de contenido a mitad de edición chocaría con el autoguardado en curso del editor.

¿Preguntas? Contáctame directamente.