Obsidian MCP Server
Gestiona notas y archivos en un vault de Obsidian. Requiere el plugin Obsidian Local REST API.
Documentación
obsidian-mcp-server
Lee, escribe, busca y edita quirúrgicamente notas, etiquetas y frontmatter de tu bóveda de Obsidian mediante MCP. STDIO o HTTP Streamable.
Descripción general
Lee, escribe, busca y edita quirúrgicamente notas de tu bóveda de Obsidian — secciones, frontmatter, etiquetas — a través del plugin Local REST API, con permisos de lectura/escritura limitados por carpeta integrados. Se ejecuta como proceso stdio o como servidor HTTP Streamable local.
Herramientas
| Herramienta | Descripción |
|---|---|
obsidian_get_note | Lee una nota como contenido bruto, forma estructurada completa (contenido + frontmatter + etiquetas + stat, con enlaces salientes opcionales), mapa documental estructural o una sola sección. |
obsidian_list_notes | Lista notas y subdirectorios bajo una ruta de la bóveda. Recorrido recursivo (profundidad predeterminada 2, máxima 20; límite de 1000 entradas) con filtros opcionales extension y nameRegex. |
obsidian_list_tags | Lista las etiquetas de la bóveda con recuentos de uso, incluidos los padres jerárquicos. Ordenadas por recuento descendente y limitadas a limit (predeterminado 200, máximo 10000), con el resto retenido divulgado. Los nameRegex y minCount opcionales reducen primero el conjunto. |
obsidian_list_commands | Lista los comandos de la paleta de comandos de Obsidian, opcionalmente filtrados por nameRegex en el nombre mostrado. Opt-in mediante OBSIDIAN_ENABLE_COMMANDS=true (emparejado con obsidian_execute_command). |
obsidian_search_notes | Busca en la bóveda por texto, JSONLogic u Omnisearch clasificado por BM25 (cuando el plugin es accesible). Los resultados se paginan mediante cursores opacos. |
obsidian_write_note | Crea una nota, reemplaza una sola sección en su lugar o — con overwrite: true — sobrescribe un archivo existente. Rechaza escrituras de archivo completo contra una ruta existente por defecto. |
obsidian_append_to_note | Añade contenido a una nota. Sin section, crea el archivo si falta. Con section, añade a un encabezado, bloque o campo de frontmatter específico (el archivo debe existir). |
obsidian_patch_note | append / prepend / replace quirúrgico contra un encabezado, referencia de bloque o campo de frontmatter. |
obsidian_replace_in_note | Reemplazo de búsqueda dentro de una sola nota, limitado al cuerpo por defecto. Coincidencia literal o regex con opciones de palabra completa, flexibilidad de espacios en blanco y sensibilidad a mayúsculas; admite reemplazo con grupos de captura. |
obsidian_manage_frontmatter | get / set / delete atómico sobre una sola clave de frontmatter. |
obsidian_manage_tags | Añade, elimina o lista etiquetas. Por defecto usa el array tags: del frontmatter; location: 'inline' o 'both' opta por mutar el cuerpo de la nota. |
obsidian_delete_note | Elimina permanentemente una nota. Siempre pide confirmación al usuario primero: la llamada se responde con una solicitud de confirmación y se reintenta con la respuesta. |
obsidian_open_in_ui | Abre un archivo en la interfaz de la aplicación Obsidian, con alternancias failIfMissing y newLeaf. |
obsidian_execute_command | Ejecuta un comando de la paleta de comandos de Obsidian por ID. Opt-in mediante OBSIDIAN_ENABLE_COMMANDS=true. |
Recursos
| Recurso | Descripción |
|---|---|
obsidian://vault/{+path} | Una nota en la bóveda: contenido, frontmatter, etiquetas y metadatos del archivo. |
obsidian://tags | Todas las etiquetas encontradas en la bóveda, con recuentos de uso (instantánea completa). |
obsidian://status | Accesibilidad del servidor, estado de autenticación, información de versión del plugin/Obsidian y extensiones de API registradas. |
Los datos de notas de la bóveda y etiquetas también son accesibles mediante herramientas: obsidian_get_note para obsidian://vault/{+path}, obsidian_list_tags para obsidian://tags (clasificadas por recuento y limitadas, a diferencia de la instantánea bruta del recurso). obsidian://status no tiene equivalente de herramienta. Los recursos existen para clientes que prefieren adjuntar una nota o instantánea de la bóveda a una conversación.
Referencia de capacidades
obsidian_get_note herramienta
format: "content" | "full" | "document-map" | "section"selecciona la proyección;fullaceptaincludeLinks: truepara enlaces wiki/markdown salientes (solo internos de la bóveda: las URL externas se filtran)- Direccionada por
pathde la bóveda, el archivoactiveo una notaperiodic(daily/weekly/monthly/quarterly/yearly) - Las secciones de encabezado usan sintaxis
Parent::Childy encuentran encabezados como lo hace el mapa documental: los encabezados setext (subrayados con===o---) cuentan, las líneas#dentro de un elemento de lista, bloque HTML o cerca no cuentan — por lo que cada ruta de encabezado que lista el mapa se lee como sí misma, sobre el mismo tramo que edita una escritura de sección; un nombre de hoja simple que coincide con varios encabezados, o una ruta completa que se repite en la nota, devuelve la primera coincidencia y lista cada ruta en conflicto encandidates - Resolución
pathtolerante: una ruta con mayúsculas/minúsculas incorrectas se reintenta contra el nombre de archivo canónico, una coincidencia ambigua falla conConflict, y unNotFoundlleva sugerenciasDid you mean: …?cuando existen coincidencias cercanas - Los errores tipados incluyen
note_missing,path_forbidden,no_active_file,periodic_unsupported/periodic_disabledypath_traversal
obsidian_list_notes herramienta
- Recorrido recursivo desde
path(raíz de la bóveda por defecto);depth1–20 (predeterminado 2 = objetivo más hijos inmediatos) - Filtros opcionales
extensionynameRegex(≤256 caracteres, sin cuantificadores anidados); un directorio que fallanameRegexse omite sin recurrir en él - Límite máximo de 1000 entradas por llamada:
excluded.reason: "entry_cap"señala un recorrido truncado; reducepatho los filtros para ver el resto truncated: truepor directorio marca entradas cortadas por el límite de profundidad o por la política de rutas
obsidian_list_tags herramienta
- Recuentos de etiquetas en toda la bóveda, incluidos los padres jerárquicos (
work/taskscontribuye tanto aworkcomo awork/tasks) - Ordenadas por recuento descendente, limitadas a
limit(predeterminado 200, máximo 10000); losnameRegexyminCountopcionales reducen el conjunto candidato antes de clasificar - Informa
truncated/shown/capcuando el límite retiene resultados - No se reduce por
OBSIDIAN_READ_PATHS: los nombres de etiquetas (nunca los contenidos de notas) pueden aparecer desde fuera del alcance de lectura
obsidian_list_commands herramienta
- Lista los IDs y nombres mostrados de la paleta de comandos de Obsidian; el
nameRegexopcional filtra por nombre mostrado - Opt-in mediante
OBSIDIAN_ENABLE_COMMANDS=true— ausente detools/listcuando no está configurado - Socio de descubrimiento para
obsidian_execute_command
obsidian_search_notes herramienta
mode: "text" | "jsonlogic"siempre;"omnisearch"se añade al esquema solo cuando el servidor HTTP del plugin Omnisearch es accesible al inicio (reinicia para volver a sondear)text— tokens separados por espacios en blanco, todos requeridos, cada uno coincidente sin distinguir mayúsculas/minúsculas como subcadena (las comillas son literales, por lo que no hay operador de frase), con ventanas de contexto de tamañocontextLength(predeterminado 100) y unpathPrefixopcional; los tokens dentro de 2 ×contextLengthentre sí, como las palabras de una frase, comparten una ubicación de coincidencia;jsonlogic— un árbol JSONLogic con rutasvarhaciapath/content/frontmatter.<key>/tags/stat.{ctime,mtime,size}, más operadoresglob/regexpque toman[PATTERN, VALUE];omnisearch— clasificado por BM25, frases entre comillas, filtros-exclusion,path:/ext:, tolerancia a errores tipográficos, PDF/OCR mediante Text Extractor, límite máximo de 50 resultados ascendentes (truncated: truecuando es probable que haya más)- Paginación por cursor: omite
cursorpara la primera página, pasanextCursorde la respuesta anterior; los resultados en modo texto además se recortan amaxMatchesPerHitubicaciones de coincidencia (predeterminado 10), marcadas contruncated/totalMatches - Sin herramienta dedicada de backlinks: expresa "qué enlaza aquí" mediante
jsonlogic:{"regexp": ["\\[\\[Target Note(\\||#|\\]\\])", {"var": "content"}]}
obsidian_write_note herramienta
- Sin
section— escritura de archivo completo; se niega a sobrescribir una nota existente a menos queoverwrite: true(conflictofile_existsen caso contrario, nombrando las herramientas de edición quirúrgica como alternativa) - Con
section—PATCH-con-reemplazo contra un objetivo de encabezado/bloque/frontmatter, dejando el resto del archivo intacto (overwritese ignora); una hoja de encabezado simple compartida por varios encabezados falla conambiguous_sectiona menos que uno de ellos no tenga encabezado padre, al que la escritura entonces apunta, y una ruta de encabezado completa que se repite en la nota falla de la misma manera - La salida informa
created, máspreviousSizeInBytes/currentSizeInBytesen cada llamada para detectar una sobrescritura accidental o una ruta mal escrita
obsidian_append_to_note herramienta
- Sin
section— añade a un archivo existente, o lo crea con el contenido dado como cuerpo completo (created: truemarca el segundo caso) - Con
section— añade a un objetivo de encabezado/bloque/frontmatter; el archivo ya debe existir, ycreateTargetIfMissing: truetrae la sección misma a la existencia. En el plugin v5.0 y posterior, el contenido añadido a un encabezado se separa del contenido existente de la sección por una línea en blanco (excepto un elemento de lista añadido a una sección que termina en una lista y no tiene subencabezados, que continúa esa lista), y un encabezado en él debe situarse por debajo del nivel de la propia sección (heading_outside_sectionen caso contrario) - Los objetivos de referencia de bloque se concatenan sin separador: incluye un salto de línea inicial en
contentpara uno previousSizeInBytes/currentSizeInBytesencierran cada llamada para detección de desviaciones
obsidian_patch_note herramienta
operation: "append" | "prepend" | "replace"contra un encabezado, referencia de bloque o campo de frontmatter por llamada; en el plugin v5.0 y posterior, el contenido añadido o antepuesto a un encabezado se separa del contenido existente de la sección por una línea en blanco (excepto un elemento de lista añadido a una sección que termina en una lista, o antepuesto a una que abre con una lista, que continúa esa lista), y un encabezado en él debe situarse por debajo del nivel de la propia sección (heading_outside_sectionen caso contrario)- Los objetivos de encabezado aceptan la ruta
Parent::Childcompleta o un nombre de hoja simple; una hoja que coincide con varios encabezados falla conambiguous_sectiony lista los candidatos, a menos que uno de ellos no tenga encabezado padre, al que el parche entonces apunta; una ruta completa que se repite en la nota falla conambiguous_sectiontambién patchOptions:createTargetIfMissing,applyIfContentPreexists(protección de idempotencia — de lo contrariocontent_preexists),trimTargetWhitespace(solo plugin v4.x; v5.0 y posterior colocan las líneas en blanco alrededor del contenido insertado por sí mismos)
obsidian_replace_in_note herramienta
- Uno o más
replacements, aplicados en orden de array, cada uno sobre la salida del anterior scope: "body"(predeterminado, frontmatter dejado byte-idéntico) |"frontmatter"|"both"; frontmatter/ambos vuelven a analizar el YAML reescrito después y no escriben nada si se rompe (frontmatter_invalid)- Opciones por reemplazo:
useRegex(≤1024 caracteres, sin cuantificadores anidados),caseSensitive,wholeWord(\b…\ben ambos modos),flexibleWhitespace(solo modo literal),replaceAll(predeterminadotrue) perReplacement[]informabodyCount/frontmatterCountpor entrada;totalReplacementslos suma
obsidian_manage_frontmatter tool
operation: "get" | "set" | "delete"en un solo campokeyde frontmatter;setrequiere unvaluecon tipo JSON (cadena, número, booleano, arreglo u objeto)getnecesita acceso de lectura;set/deletenecesitan la ruta dentro deOBSIDIAN_WRITE_PATHSconOBSIDIAN_READ_ONLY=falseset/deletedevuelven elfrontmattercompleto después del cambio máspreviousSizeInBytes/currentSizeInBytes
obsidian_manage_tags tool
operation: "add" | "remove" | "list";location: "frontmatter"(predeterminado, arreglo canónico detags:) |"inline"(cuerpo#tag,addagrega al final del archivo) |"both"(concilia ambos)- La detección en línea omite código (delimitado, indentado y en línea), wikilinks (
[[...]]), imágenes, el destino o la etiqueta de un enlace Markdown (su texto se lee), bloques y comentarios HTML, y matemáticas ($…$,$$…$$), por lo que un ancla de encabezado o un alias de wikilink nunca se confunde con una etiqueta; los comentarios%% … %%aún se leen, tal como los lee Obsidian - Las etiquetas en línea siguen la gramática de Obsidian: una etiqueta comienza al inicio de la línea, después de espacios en blanco, después de otra etiqueta (
#a#bson dos etiquetas), o justo después de marcado como**,_…_,==,[, el|de una celda de tabla,<br>, o un escape de\(**#x**es una etiqueta;(#x,.#x,a *#xy\#xno lo son) y continúa con letras y dígitos en cualquier escritura, emojis,_,-y/, con al menos un carácter que no sea un dígito ASCII (#1990s,#café,#日本語y#✅doneson etiquetas;#1984no lo es) add/removeinforman etiquetasappliedvs.skippedmás el conjunto completo detagsdespués del cambio;listignora el arreglo detagsde entrada
obsidian_delete_note tool
- Siempre pide confirmación primero: la llamada inicial devuelve una solicitud de obtención que indica el tamaño del archivo en bytes, y se reintenta con la respuesta; rechazar falla con
cancelledy no emite ningúnDELETE - No hay deshacer a nivel de API: la recuperación requiere la papelera local de Obsidian
- Requiere un cliente MCP que pueda manejar un ciclo de ida y vuelta de obtención; todas las demás herramientas funcionan sin uno
obsidian_open_in_ui tool
failIfMissing(predeterminadotrue) controla abrir-vs-crear: abrir un archivo existente necesita acceso de lectura, abrir uno faltante (confailIfMissing: false) lo crea y necesita acceso de escrituranewLeafabre en un panel dividido en lugar del activo- Misma resolución de ruta indulgente que
obsidian_get_note(respaldo de mayúsculas, sugerencias deDid you mean);obsidian_delete_notedeliberadamente no la recibe: una operación destructiva nunca reescribe silenciosamente su objetivo - La salida informa
createdIfMissingpara que el llamador pueda saber qué rama se ejecutó
obsidian_execute_command tool
- Despacha un comando de la paleta de comandos de Obsidian por
commandId(descúbralo medianteobsidian_list_commands); se ejecuta con la misma autoridad que una invocación de teclado - Opt-in mediante
OBSIDIAN_ENABLE_COMMANDS=true— ausente detools/listcuando no está configurado - El comportamiento depende del comando: algunos son destructivos (eliminar archivo, cerrar bóveda), algunos abren interfaz de usuario
obsidian://vault/{+path} resource
- El segmento
{+path}captura todo después de/vault/, incluidas las barras - Las rutas pueden enviarse literalmente o con codificación porcentual:
Folder/Test Note.mdyFolder/Test%20Note.mdse resuelven a la misma nota, al igual que los nombres no ASCII y un%simple - Devuelve la misma forma que
obsidian_get_noteconformat: "full"— contenido, frontmatter, etiquetas, stat - Controlado por
OBSIDIAN_READ_PATHS/OBSIDIAN_WRITE_PATHScomo el equivalente de herramienta
obsidian://tags resource
- Instantánea completa de la carga útil de
/tags/ascendente — sin ordenar, sin límite, incluye padres jerárquicos - No es un espejo de
obsidian_list_tags: sin orden descendente por recuento, sinlimit/nameRegex/minCount
obsidian://status resource
- Accesibilidad, versión del plugin,
authenticated(si elOBSIDIAN_API_KEYconfigurado fue aceptado) e información del manifiesto del plugin apiExtensions[]enumera las extensiones de plugin registradas: verifiquelocal-rest-api-periodic-notesantes de confiar en objetivos deperiodicen el plugin v5.0.2 y posteriores- Aún informa accesibilidad cuando la clave API está mal configurada; solo
authenticatedrefleja la validez de la clave
Política de rutas (permisos limitados por carpeta)
Tres variables de entorno opcionales controlan qué rutas de bóveda puede apuntar cada herramienta. Predeterminado sin configurar = bóveda completa tanto para lecturas como para escrituras — compatible con versiones anteriores.
| Objetivo | Configuración |
|---|---|
| Predeterminado (comportamiento actual) | todo sin configurar |
Leer en todas partes, escribir solo en projects/ y scratch/ | OBSIDIAN_WRITE_PATHS=projects/,scratch/ |
Leer solo public/, escribir solo public/inbox/ | OBSIDIAN_READ_PATHS=public/, OBSIDIAN_WRITE_PATHS=public/inbox/ |
| Implementación de solo lectura — sin escrituras en ningún lugar | OBSIDIAN_READ_ONLY=true |
La coincidencia se basa en prefijos con recursión implícita, sin distinción de mayúsculas, con barras finales normalizadas. projects/ coincide con projects/a.md, projects/sub/b.md, etc.
Las rutas de escritura son implícitamente legibles — no se puede editar de manera sensata lo que no se puede ver. Por lo tanto, una lectura pasa cuando el objetivo coincide con READ_PATHS o WRITE_PATHS.
OBSIDIAN_READ_ONLY=true se cortocircuita antes de las verificaciones de ruta — cada herramienta de escritura y el par de paleta de comandos están envueltos con disabledTool() al inicio (ausente de tools/list), y cualquier escritura que aún llegue al servicio se deniega en tiempo de ejecución independientemente de WRITE_PATHS.
Las denegaciones se tipan como path_forbidden (código JSON-RPC Forbidden) con el alcance activo reflejado en data.recovery.hint y data.activeScope, para que el LLM pueda autocorregirse sin inspeccionar los registros del servidor. Los resultados de búsqueda de obsidian_search_notes se filtran contra READ_PATHS silenciosamente — mostrar un indicador de "ocultamos N coincidencias" anularía la protección.
El listado de etiquetas es de toda la bóveda. obsidian_list_tags y el recurso obsidian://tags agregan nombres de etiquetas en toda la bóveda y no se limitan por OBSIDIAN_READ_PATHS — no toman ninguna ruta para filtrar, por lo que los nombres de etiquetas (nunca los contenidos de notas) fuera del alcance de lectura pueden aparecer.
El banner de inicio registra el alcance activo para que los operadores puedan verificar su configuración al arrancar.
Características
Construido sobre @cyanheads/mcp-ts-core: transportes stdio y Streamable HTTP, autenticación conectable (none / jwt / oauth), almacenamiento intercambiable (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), registro estructurado con rastreo opcional de OpenTelemetry.
Específico de Obsidian:
- Envuelve el plugin Obsidian Local REST API — cliente tipado, mapeo de errores determinista
- Edición consciente de secciones en encabezados, referencias de bloque y campos de frontmatter mediante operaciones
PATCH-con-objetivo - Búsqueda en tres modos — texto, JSONLogic y (cuando está accesible) Omnisearch clasificado por BM25 — paginada por cursor según la especificación MCP 2025-11-25
- Reconciliación de etiquetas en ambas representaciones: arreglo de frontmatter
tags:y sintaxis en línea#tag - Permisos de lectura/escritura limitados por carpeta mediante
OBSIDIAN_READ_PATHS/OBSIDIAN_WRITE_PATHSy un interruptor de apagado globalOBSIDIAN_READ_ONLY; par de paleta de comandos opt-in controlado porOBSIDIAN_ENABLE_COMMANDS.instructionsa nivel de servidor eninitializeinforman la política activa al llamador
Salida amigable para agentes:
- Errores guiados por recuperación — cada fallo declarado lleva un
reason, un código JSON-RPC y unrecovery.hintescrito para ese caso, de modo que un rechazo nombra qué hacer a continuación en lugar de solo qué se rompió - Autocorrección por delta de tamaño — cada herramienta mutante devuelve
previousSizeInBytes/currentSizeInBytes, para que un llamador pueda detectar una sobrescritura accidental o un comportamiento ascendente inesperado sin una lectura de seguimiento - Ambigüedad expuesta estructuralmente — un nombre de hoja de encabezado compartido por varios encabezados devuelve
candidatesen lugar de elegir uno silenciosamente; las operaciones de etiquetas informanappliedvs.skippedpara que un llamador vea exactamente qué cambió - Contratos de salida discriminados —
formatenobsidian_get_note,operationenobsidian_manage_frontmatteryobsidian_manage_tags,modeenobsidian_search_notes— los llamadores ramifican en campos tipados en lugar de analizar texto
Primeros pasos
Agregue lo siguiente a su archivo de configuración del cliente MCP. El plugin Obsidian Local REST API debe estar instalado y habilitado en su bóveda — consulte Prerrequisitos.
{
"mcpServers": {
"obsidian-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["obsidian-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info",
"OBSIDIAN_API_KEY": "your-local-rest-api-key"
}
}
}
}
O con npx (sin necesidad de Bun):
{
"mcpServers": {
"obsidian-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "obsidian-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info",
"OBSIDIAN_API_KEY": "your-local-rest-api-key"
}
}
}
}
O con Docker:
{
"mcpServers": {
"obsidian-mcp-server": {
"type": "stdio",
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "MCP_TRANSPORT_TYPE=stdio",
"-e", "MCP_LOG_LEVEL=info",
"-e", "OBSIDIAN_API_KEY=your-local-rest-api-key",
"ghcr.io/cyanheads/obsidian-mcp-server:latest"
]
}
}
}
El OBSIDIAN_BASE_URL predeterminado (http://127.0.0.1:27123) apunta al loopback del propio contenedor, no al de su host — agregue -e OBSIDIAN_BASE_URL=http://host.docker.internal:27123 (Docker Desktop) o ejecute con --network host (Linux) para que el contenedor pueda alcanzar el plugin.
Para Streamable HTTP, configure el transporte e inicie el servidor. Las variables de entorno en línea funcionan para ejecuciones únicas; para uso repetido, copie los valores en .env (consulte .env.example) y ejecute bun run start:http.
MCP_TRANSPORT_TYPE=http OBSIDIAN_API_KEY=... bun run start:http
# Server listens at http://127.0.0.1:3010/mcp by default
Prerrequisitos
- Bun v1.4.0 o superior (o Node.js v24+).
- El plugin Obsidian Local REST API, v4.0.0 o posterior, instalado y habilitado en su bóveda. Genere una clave API en Configuración → Plugins de la comunidad → Local REST API y cópiela en
OBSIDIAN_API_KEY. Las escrituras dirigidas a secciones y el mapa de documentos hablan markdown-patch 2.0 con el plugin v5.0 y posterior, y el formato 1.x con v4.x; el servidor lee la versión del plugin una vez y elige el formato por sí mismo. Dos escrituras de filas de tabla (contentType: "json") que markdown-patch 2.0 no puede expresar se envían como 1.x también en v5.x: filas escritas bajo un encabezado, y filas escritas mediante un ID de bloque en su propia línea debajo de la tabla. El plugin v6.0 elimina 1.x, por lo que en v6.0 esas dos formas fallan; apunte a la tabla mediante un ID en su última fila en su lugar. - Los objetivos de notas periódicas (
target: { "type": "periodic" }) funcionan en todo ese rango: nativamente en el plugin v5.0.1 y anteriores, y en v5.0.2 y posteriores — que movió las rutas de/periodic/fuera del plugin — una vez que la extensión complementaria periodic-notes API extension esté instalada. Sin esa extensión en v5.0.2+, los objetivos periódicos fallan con un errorperiodic_unsupportedque la nombra;obsidian://statusenumera las extensiones registradas si desea verificar primero. Todos los demás tipos de objetivos no se ven afectados. - Un cliente MCP que pueda responder una solicitud de entrada (obtención).
obsidian_delete_notesiempre pide confirmación antes de eliminar, por lo que un cliente sin ese soporte puede leer y escribir notas pero no puede eliminar una. - Este servidor usa
http://127.0.0.1:27123de forma predeterminada por simplicidad. Habilite "Servidor no cifrado (HTTP)" en la configuración del plugin para usarlo. Para usar el puerto HTTPS siempre activo en su lugar, configureOBSIDIAN_BASE_URL=https://127.0.0.1:27124; el certificado autofirmado del plugin lo manejaOBSIDIAN_VERIFY_SSL=false(el predeterminado), que relaja la verificación para las solicitudes de este servidor solo a ese endpoint.
Instalación
-
Clone el repositorio:
git clone https://github.com/cyanheads/obsidian-mcp-server.git -
Navegue al directorio:
cd obsidian-mcp-server -
Instale las dependencias:
bun install -
Configure el entorno:
cp .env.example .env # edit .env and set OBSIDIAN_API_KEY
Configuración
| Variable | Descripción | Predeterminado |
|---|---|---|
OBSIDIAN_API_KEY | Requerido. Token Bearer para el plugin Obsidian Local REST API. | — |
OBSIDIAN_BASE_URL | URL base del plugin Local REST API. Usa https://127.0.0.1:27124 para el puerto HTTPS siempre activo (certificado autofirmado). La barra final se elimina al inicio. Cuando no hay respuesta allí (Obsidian cerrado, plugin deshabilitado, host o puerto incorrectos), las llamadas fallan con obsidian_unreachable — un GET, PUT o DELETE después de sus reintentos, cualquier otra solicitud en el primer intento. | http://127.0.0.1:27123 |
OBSIDIAN_VERIFY_SSL | Verifica el certificado TLS. El valor predeterminado es false porque el plugin usa un certificado autofirmado. La relajación se aplica por solicitud, solo a un https: OBSIDIAN_BASE_URL — cualquier otra conexión HTTPS que el proceso realice sigue verificándose normalmente, tanto en Bun como en Node. Con true, un certificado que el runtime no confía hace fallar cada llamada en su primer intento con certificate_rejected. | false |
OBSIDIAN_REQUEST_TIMEOUT_MS | Tiempo de espera por solicitud en milisegundos. | 30000 |
OBSIDIAN_ENABLE_COMMANDS | Indicador opcional para el par de paleta de comandos (obsidian_list_commands + obsidian_execute_command). Desactivado por defecto: los comandos de Obsidian son opacos y pueden ser destructivos. | false |
OBSIDIAN_READ_PATHS | Lista de carpetas permitidas relativas al vault, separadas por comas, para operaciones de lectura. Basada en prefijos con recursión implícita; insensible a mayúsculas; barras finales normalizadas. Sin definir = vault completo. Las rutas de escritura son implícitamente legibles. | sin definir |
OBSIDIAN_WRITE_PATHS | Lista de carpetas permitidas relativas al vault, separadas por comas, para operaciones de escritura. Misma sintaxis que OBSIDIAN_READ_PATHS. Sin definir = vault completo. | sin definir |
OBSIDIAN_READ_ONLY | Interruptor global de apagado. Cuando true, deniega toda escritura independientemente de OBSIDIAN_WRITE_PATHS y suprime el par OBSIDIAN_ENABLE_COMMANDS (los comandos pueden mutar). | false |
OBSIDIAN_OMNISEARCH_URL | URL de anulación para el servidor HTTP del plugin Omnisearch. Cuando no está definido, se deriva del host de OBSIDIAN_BASE_URL con el puerto 51361 (con respaldo a http://localhost:51361). Se sondea una vez al inicio: si es accesible, el modo omnisearch se agrega a obsidian_search_notes; de lo contrario, se omite del esquema de herramientas. Reinicia el servidor para volver a sondear. | derivado |
MCP_TRANSPORT_TYPE | Transporte: stdio o http. | stdio |
MCP_HTTP_HOST | Host para el servidor HTTP. | 127.0.0.1 |
MCP_HTTP_PORT | Puerto para el servidor HTTP. | 3010 |
MCP_HTTP_ENDPOINT_PATH | Ruta del endpoint para el manejador JSON-RPC. | /mcp |
MCP_SESSION_MODE | Manejo de sesiones para el transporte HTTP: stateless, stateful o auto. Aquí el valor predeterminado es stateful — obsidian_delete_note confirma mediante una ronda de elicitación, y bajo stateless la ronda de un cliente de la era 2025 es rechazada (client_capability_missing). | stateful |
MCP_PUBLIC_URL | Anulación de origen público para despliegues con proxy inverso que termina TLS (página de inicio, tarjeta de servidor, metadatos RFC 9728). | sin definir |
MCP_AUTH_MODE | Modo de autenticación: none, jwt o oauth. | none |
MCP_AUTH_SECRET_KEY | Requerido cuando MCP_AUTH_MODE=jwt. Secreto compartido de ≥32 caracteres usado para verificar los JWT entrantes. | — |
MCP_AUTH_DISABLE_SCOPE_CHECKS | Cuando true, omite la aplicación de alcance por herramienta después de la verificación de presencia del contexto de autenticación. La firma del token, la audiencia, el emisor y la validación de expiración permanecen intactos. Úsalo solo cuando no se pueda inyectar una claim personalizada y combínalo con OBSIDIAN_READ_PATHS / OBSIDIAN_WRITE_PATHS / OBSIDIAN_READ_ONLY para el control de acceso. Se registra un WARNING al inicio siempre que el bypass esté activo. | false |
MCP_LOG_LEVEL | Nivel de registro (RFC 5424). | info |
LOGS_DIR | Directorio para archivos de registro (solo Node.js). | <project-root>/logs |
OTEL_ENABLED | Habilita instrumentación OpenTelemetry (spans, métricas, registros de finalización). | false |
Consulta .env.example para la lista completa de anulaciones opcionales.
Ejecutar el servidor
Desarrollo local
-
Compilar y ejecutar la versión de producción:
# One-time build bun run rebuild # Run the built server bun run start:stdio # or bun run start:http -
Ejecutar verificaciones y pruebas:
bun run devcheck # Lint, format, typecheck, security, changelog sync bun run test # Vitest test suite bun run lint:mcp # Validate MCP definitions against spec
Docker
docker build -t obsidian-mcp-server .
docker run --rm -e OBSIDIAN_API_KEY=your-key -p 3010:3010 obsidian-mcp-server
El Dockerfile usa por defecto transporte HTTP, modo de sesión con estado (requerido para la ronda de confirmación obsidian_delete_note) y registra en /var/log/obsidian-mcp-server. Apunta OBSIDIAN_BASE_URL a http://host.docker.internal:27123 (Docker Desktop) o ejecuta con --network host (Linux) para que el contenedor alcance el plugin en tu host. Las dependencias opcionales de OpenTelemetry se instalan por defecto: compila con --build-arg OTEL_ENABLED=false para omitirlas.
La imagen se vincula a 0.0.0.0 dentro del contenedor (requerido para el mapeo de puertos de Docker). Para cualquier despliegue accesible más allá de tu propia máquina, establece MCP_AUTH_MODE=jwt (con MCP_AUTH_SECRET_KEY) o oauth — de lo contrario, el listener reenvía tu OBSIDIAN_API_KEY al vault en nombre de cada llamador.
Estructura del proyecto
| Directorio | Propósito |
|---|---|
src/index.ts | Punto de entrada de createApp() — registra herramientas/recursos e inicializa el servicio de Obsidian. |
src/config | Análisis de variables de entorno específicas del servidor (OBSIDIAN_*) con Zod. |
src/services/obsidian | Cliente de Local REST API, operaciones de frontmatter, extractor de secciones, tipos de dominio. |
src/mcp-server/tools | Definiciones de herramientas (*.tool.ts) y esquemas de entrada compartidos. |
src/mcp-server/resources | Definiciones de recursos (*.resource.ts). |
src/mcp-server/prompts | Definiciones de prompts (actualmente vacías — la forma CRUD/búsqueda no se beneficia de una plantilla estructurada). |
tests/ | Pruebas Vitest que reflejan src/. |
docs/ | Especificación OpenAPI ascendente para el plugin Local REST API y el tree.md generado. |
changelog/ | Notas de versión por versión; CHANGELOG.md es el resumen regenerado. |
Guía de desarrollo
Consulta CLAUDE.md para las pautas de desarrollo y reglas arquitectónicas. La versión corta:
- Los manejadores lanzan, el framework captura — sin
try/catchen la lógica de herramientas - Usa
ctx.logpara registro con ámbito de solicitud,ctx.statepara almacenamiento con ámbito de tenant - Registra nuevas herramientas y recursos mediante los barriles en
src/mcp-server/*/definitions/index.ts - Envuelve las llamadas a API externas: valida el dato crudo → normaliza al tipo de dominio → devuelve el esquema de salida; nunca inventes campos faltantes
Contribuciones
Errores, solicitudes de funciones y brechas de documentación pertenecen a un issue — consulta CONTRIBUTING.md para saber qué hace que uno sea accionable, y CODE_OF_CONDUCT.md para saber cómo trabajamos juntos. Los informes de seguridad pasan por SECURITY.md, nunca por un issue público.
Ejecuta verificaciones y pruebas antes de enviar:
bun run devcheck
bun run test
Licencia
Apache-2.0 — consulta LICENSE para más detalles.