Skip to main content
Glama
RasAlGhul96

Obsidian MCP Server

by RasAlGhul96
README.md
# Obsidian MCP Server (Zero Trust)

Servidor MCP en TypeScript que conecta Claude Desktop con una boveda local de Obsidian
bajo un modelo de **Confianza Cero**. Es **solo lectura por defecto**; la escritura es
**opt-in** mediante `OBSIDIAN_ENABLE_WRITE=true` y pasa por el mismo sandbox.

## Herramientas expuestas

| Herramienta    | Tipo         | Descripcion                                          |
|----------------|--------------|------------------------------------------------------|
| `read_note`    | lectura      | Lee el contenido de una nota `.md` de la boveda.     |
| `list_notes`   | lectura      | Lista las notas de la boveda (o de una subcarpeta).  |
| `search_vault` | lectura      | Busca texto dentro de las notas de la boveda.        |
| `create_note`  | escritura *  | Crea una nota `.md` nueva. Falla si ya existe.       |
| `update_note`  | escritura *  | Sobrescribe una nota existente (escritura atomica).  |
| `append_note`  | escritura *  | Anade texto al final de una nota existente.          |
| `delete_note`  | escritura *  | Mueve una nota a la papelera `.trash` (reversible).  |

\* Las herramientas de escritura solo se registran si `OBSIDIAN_ENABLE_WRITE=true`.
Por defecto el servidor es **solo lectura**.

## Modelo de seguridad (Zero Trust)

El servidor asume que **toda ruta recibida es hostil** hasta ser demostrada segura.
El unico limite de confianza es `OBSIDIAN_VAULT_PATH` (ruta absoluta).

Garantias del sandbox:

1. **Anti path traversal** — se resuelve la ruta a su forma canonica absoluta y se
   verifica que siga estando *dentro* de la boveda. Se bloquea `../../etc/passwd`,
   `..\\..\\.ssh\\id_rsa`, rutas absolutas externas, null bytes, etc.
2. **Ocultos ignorados** — cualquier segmento que empiece por `.` (p. ej. `.obsidian`,
   `.git`, `.ssh`) queda vetado. La configuracion interna de Obsidian nunca es accesible.
   Ademas se rechaza `:` (unidad relativa `C:foo` y flujos de datos alternativos NTFS).
3. **Enlaces simbolicos confinados** — se resuelve el destino real del symlink
   (`fs.realpath`) y se rechaza si escapa de la boveda, evitando fugas por enlaces.
4. **Allowlist de extensiones** — solo se leen archivos `.md` (y `.markdown`).
5. **Un unico portero** — cada herramienta MUST pasar por el middleware `resolveSafePath`
   antes de tocar el disco. Ninguna herramienta accede al filesystem por su cuenta.

## Estructura del proyecto

```
MCP/
├── src/
│   ├── index.ts            # Entrypoint: crea el server MCP + transporte stdio (Fase 2)
│   ├── config/
│   │   └── env.ts          # Carga y valida OBSIDIAN_VAULT_PATH (Fase 2)
│   ├── security/
│   │   └── pathGuard.ts    # Middleware Zero Trust de validacion de rutas (Fase 2)
│   └── tools/
│       ├── readNote.ts     # Herramienta read_note (Fase 2)
│       ├── listNotes.ts    # Herramienta list_notes (Fase 2)
│       └── searchVault.ts  # Herramienta search_vault (Fase 2)
├── tests/
│   └── security.test.ts    # Red Team: intentos de path traversal, ocultos, symlinks (Fase 3)
├── package.json
├── tsconfig.json
├── .env.example
└── .gitignore
```

## Diseno del middleware de seguridad: `resolveSafePath`

Contrato de la funcion central que blindara cada herramienta en la Fase 2:

```
resolveSafePath(relativeInput: string): string  // devuelve ruta absoluta segura o LANZA error
```

Pipeline de validacion (falla-cerrado, se rechaza ante cualquier duda):

1. **Normalizar entrada** — rechazar si contiene `\0` (null byte) o esta vacia.
2. **Prohibir rutas absolutas del cliente** — el input siempre es *relativo a la boveda*.
   Se rechaza `path.isAbsolute(input)` y esquemas tipo `C:\`, `/`, `\\servidor`.
3. **Resolver contra la boveda** — `path.resolve(VAULT_ROOT, input)`.
4. **Verificar contencion** — la ruta resuelta debe empezar por `VAULT_ROOT + path.sep`
   (comparacion normalizada, case-insensitive en Windows). Si no, `PATH_ESCAPE`.
5. **Vetar segmentos ocultos** — dividir la ruta relativa por separador y rechazar si
   algun segmento empieza por `.`.
6. **Resolver symlinks reales** — `fs.realpathSync` del destino y repetir el paso 4
   sobre la ruta real. Si el enlace apunta fuera, `SYMLINK_ESCAPE`.
7. **Validar extension** — para lecturas de archivo, exigir `.md` / `.markdown`.

Errores estructurados (nunca stack traces crudos al modelo):
`PATH_ESCAPE`, `HIDDEN_SEGMENT`, `SYMLINK_ESCAPE`, `INVALID_EXTENSION`, `NOT_FOUND`.

## Requisitos

- Node.js >= 20
- Una boveda de Obsidian local

## Estado

- [x] Fase 1 — Arquitectura y entorno
- [x] Fase 2 — Core y herramientas
- [x] Fase 3 — Red Team y tests de seguridad (18/18)
- [x] Fase 4 — Despliegue e integracion

TDQS

A4.4/5.0

Scored across 3 tools

Disambiguation5/5

Each tool serves a clearly distinct purpose: reading a single note, listing notes recursively, and searching for content across notes. There is no overlap or ambiguity between them.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern: read_note, list_notes, search_vault. The naming convention is uniform and predictable.

Tool Count5/5

Three tools is well-scoped for a read-only Obsidian vault server. Each tool earns its place and covers the essential read/query operations without redundancy.

Completeness5/5

For a read-only server, the surface is complete: you can read, list, and search notes. There are no obvious gaps for the stated purpose of accessing and searching vault content.

Maintenance

ActivitySlowing
ResponsivenessNo issues