Skip to main content
Glama
aliasunder

Vault Cortex Obsidian MCP Server

CI Gitleaks Trivy GitHub Release npm OpenSSF Scorecard OpenSSF Best Practices Ask DeepWiki vault-cortex MCP server

Vault Cortex는 모든 AI 에이전트에게 Obsidian 볼트에 대한 하이브리드 검색, 작업 관리, 구조화된 메모리, 읽기/쓰기 액세스를 제공하는 독립형 MCP 서버입니다. 플러그인도, 실행 중인 Obsidian도, 별도의 브리지도 필요 없습니다. Docker 컨테이너 하나, 볼트 폴더, 전체 도구 모음 + 가이드 프롬프트만 있으면 됩니다. Obsidian Sync와 함께 원격 서버에서 실행하면, 동일한 볼트를 휴대폰, claude.ai 또는 모든 원격 MCP 클라이언트에서 OAuth 2.1로 보호된 상태로 접근할 수 있습니다. 원클릭으로 배포하거나 직접 호스팅하세요. 어느 쪽이든 볼트는 항상 당신의 것입니다.

목차 — 제공 기능 · 빠른 시작 · 작동 방식 · 하이브리드 검색 · 메모리 · 작업 · 파일 · 도구 · 프롬프트 · 속성 · 설정 · 데일리 노트 · 데이터 무결성 · 인증 · 배포 · 원클릭 배포 · 커뮤니티 배포

제공 기능

  • 원격 액세스 — OAuth 2.1을 통해 휴대폰, 원격 서버 또는 모든 MCP 클라이언트에서 작동합니다. Render나 Railway에서 한 번의 클릭으로 서버 관리 없이 시작할 수 있으며, VPS도 가능합니다.

  • 플러그인 불필요 — Obsidian이 실행 중일 필요가 없습니다. 서버는 디스크의 .md 파일과 직접 작동합니다. 헤드리스 동기화가 볼트를 최신 상태로 유지합니다.

  • 하이브리드 검색 — FTS5 키워드 매칭 + RRF 융합을 통한 벡터 의미 유사도, 의도가 강한 쿼리는 교차 인코더 재순위화로 정밀도를 높입니다. 키워드는 정확한 용어와 전문 용어에서 정밀도를 유지하고, 벡터는 볼트의 단어와 다른 표현을 사용해도 노트를 찾아냅니다.

  • 구조화된 메모리 — 날짜가 기록된 추가 전용 항목이 개인 지식 계층으로 축적되며, AI 개인화를 위해 자동 초기화됩니다. 주제 회상은 "X에 대해 어떻게 생각하나요?"라는 질문에 현재 관점과 그 뒤에 있는 날짜별 이력을 포함해 답합니다 — 진화 과정까지 포함됩니다.

  • 작업 — 칸반 인식 작업 쿼리 및 업데이트: 상태, 날짜 또는 우선순위로 분류한 후 한 번의 호출로 작업을 완료, 우선순위 변경 또는 레인 간 이동할 수 있습니다. Tasks 플러그인 이모지와 Dataview 인라인 필드 형식을 모두 파싱합니다.

  • 링크 그래프 — 볼트 전체의 역링크, 외부 링크, 고아 노트 감지

  • 파일 — 볼트의 마크다운이 아닌 파일도 읽습니다: 이미지는 실제 이미지로 제공되고(필요 시 크기 축소), PDF는 구조화된 텍스트 또는 렌더링된 페이지로, 캔버스는 읽기 가능한 개요로, 데이터 파일은 텍스트로 제공됩니다

  • Obsidian 네이티브 — frontmatter, wikilink, 태그, 제목, 데일리 노트를 이해합니다

  • 가이드 워크플로우 — 볼트 상태 점검, 메모리 검토, 일일 정리를 위한 내장 프롬프트 — 매번 실시간 볼트 데이터로 구성됩니다

유럽 15일 여행에서 테스트 완료. 휴대폰에서 30개 이상의 세션, 216회의 도구 호출, 노트북 접근 전혀 불필요. 한 세션에서의 쓰기가 다음 세션에서 즉시 사용 가능했으며, 도시와 날짜를 넘나들며 확인되었습니다.


Related MCP server: Vault MCP Server (mschuchard)

빠른 시작

로컬 (2분 — Docker + 볼트 폴더)

사전 요구 사항: Docker (또는 Docker 호환 런타임, 예: OrbStack, Colima, Podman), Node.js >= 20.12 (CLI 전용 — 서버 자체는 Docker에서 실행됨), 그리고 Obsidian 볼트 (또는 .md 파일이 있는 모든 폴더).

npx vault-cortex@latest init

이것으로 끝입니다 — CLI가 볼트 경로를 묻고, 인증 토큰과 설정 파일을 생성하고, 서버를 시작하고, MCP 클라이언트용 연결 정보를 출력합니다 (CLI 참조 →).

npx vault-cortex@latest init — 대화형 설정 마법사가 모드를 선택하고, 볼트를 찾고, 선택적 설정을 제안하고, 설정을 생성하고, 서버를 시작합니다

CLI로 설정하셨나요? 이제부터 CLI가 서버를 관리합니다 — configure, upgrade, start, restart, logs, down (CLI 참조 →).

Compose로 설정하셨나요? 업데이트도 Compose로 계속하세요 (docker compose pull && docker compose up -d) — CLI와 Compose는 컨테이너를 독립적으로 관리합니다.

# 1. Get the quickstart files
curl -O https://raw.githubusercontent.com/aliasunder/vault-cortex/main/deploy/local/docker-compose.yml
curl -O https://raw.githubusercontent.com/aliasunder/vault-cortex/main/deploy/local/.env.example

# 2. Configure
cp .env.example .env
# Edit .env — set MCP_AUTH_TOKEN (openssl rand -hex 32) and VAULT_PATH

# 3. Start
docker compose up

전체 로컬 가이드 → (Windows 설정 포함)

원격 (어디서나 접근)

Obsidian Sync로 최신 상태를 유지하는 서버의 볼트를 휴대폰, claude.ai 또는 모든 MCP 클라이언트에서 접근할 수 있습니다. 원클릭 옵션은 Obsidian Sync 토큰, 볼트 이름, 시간대를 묻고(볼트가 암호화된 경우 볼트 비밀번호도), HTTPS, 재시작, 생성된 MCP 토큰, 볼트와 인덱스의 영구 저장소를 처리합니다. 자체 서버에서는 CLI가 공개 URL과 볼트 이름을 묻고, Sync 토큰을 대신 캡처하며, MCP 토큰을 생성합니다. HTTPS는 직접 설정해야 합니다.

Railway

Render

자체 호스팅

Deploy on Railway

Deploy to Render

CLI 설정 →

계정

Railway Hobby 플랜 이상 — 5GB 볼륨 포함

Render 카드 등록 필요

Docker가 설치된 VPS

비용

사용량 기준: 개인 볼트 기준 월 $20–30 USD — 조용한 볼트는 Render보다 약간 저렴하고, 사용량이 많으면 약간 비쌉니다

고정 요금: Standard 인스턴스(2GB) 및 5GB 디스크 기준 월 약 $26 USD, 초 단위 과금

VPS 비용에 따라 다름

선택 기준

더 쉬운 시작을 원한다면 — 템플릿이 구성된 프로젝트로 안내합니다

예측 가능한 청구 금액이 설정 편의성보다 중요하다면

이미 서버를 운영 중이거나 완전한 제어를 원한다면

가이드

Railway 가이드 →

Render 가이드 →

원격 가이드 →

세 가지 모두 Obsidian Sync 구독이 필요합니다. 어느 것을 선택하든 서버는 교체 가능하고 볼트는 그렇지 않습니다 — 볼트는 Obsidian Sync와 기기에서 일반 Markdown으로 유지되며, 컨테이너는 복사본만 보관합니다.

자체 호스팅: 직접 운영하는 VPS

vault-cortex CLI는 실행 중인 모든 Linux 머신에 동일한 컨테이너를 설정합니다 — 서버, 이미지, 업데이트를 직접 관리합니다. CLI 자체에는 Node.js >= 20.12가 필요하며, 서버는 Docker에서 실행됩니다.

# On your VPS:
npx vault-cortex@latest init --mode remote

이것으로 끝입니다 — CLI가 공개 URL, Obsidian Sync 토큰(대신 get-sync-token을 실행할 수 있음), 볼트 이름, 암호화된 볼트의 볼트 비밀번호, 인증 설정을 안내한 후 서버를 시작합니다 (CLI 참조 →).

CLI로 설정하셨나요? 이제부터 CLI가 서버를 관리합니다 — configure, upgrade, start, restart, logs, down (CLI 참조 →).

Compose로 설정하셨나요? 업데이트도 Compose로 계속하세요 (docker compose pull && docker compose up -d) — CLI와 Compose는 컨테이너를 독립적으로 관리합니다.

# On your VPS:
mkdir -p /opt/vault-cortex && cd /opt/vault-cortex
curl -O https://raw.githubusercontent.com/aliasunder/vault-cortex/main/deploy/remote/docker-compose.yml
curl -O https://raw.githubusercontent.com/aliasunder/vault-cortex/main/deploy/remote/.env.example
cp .env.example .env
# Edit .env — set MCP_AUTH_TOKEN, PUBLIC_URL, OBSIDIAN_AUTH_TOKEN, VAULT_NAME
docker compose up -d

MCP 클라이언트 연결

설정

서버 URL

로컬

http://localhost:8000/mcp

원격 (원클릭)

https://<host>/mcp — <host>는 Render 또는 Railway가 서비스 페이지에 표시하는 도메인입니다

원격 (자체 호스팅)

<PUBLIC_URL>/mcp

MCP 클라이언트(Claude Code, Claude Desktop, Cursor, OpenCode 또는 기타)에 서버 URL을 추가하세요. OAuth 클라이언트는 브라우저에서 동의 페이지를 엽니다 — 토큰으로 승인하면 이후 클라이언트가 토큰 갱신을 처리합니다. OAuth가 없는 클라이언트(MCP Inspector, 스크립트)는 Authorization: Bearer 헤더로 토큰을 직접 전송합니다.

Claude Code:

claude mcp add --scope user --transport http vault-cortex http://localhost:8000/mcp   # local (or <PUBLIC_URL>/mcp)

--scope user는 모든 프로젝트에 서버를 등록합니다. 이를 생략하면 현재 디렉토리에만 범위가 제한됩니다.

"Add custom connector" 대화상자는 https URL만 허용합니다. https PUBLIC_URL이 있으면 커넥터 대화상자에 직접 추가하세요. localhost 서버의 경우 mcp-remote stdio 브리지를 통해 claude_desktop_config.json에 등록하세요:

{
  "mcpServers": {
    "vault-cortex": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "http://localhost:8000/mcp",
        "--header",
        "Authorization: Bearer <your MCP_AUTH_TOKEN>"
      ]
    }
  }
}

claude.ai (웹 및 모바일) 은 원격 설정에만 연결됩니다 — 커넥터가 서버 측에서 가져와지므로 localhost에는 절대 도달할 수 없습니다.

"원격 MCP 서버"는 연결 유형(HTTP)을 의미합니다 — 로컬 설정에서도 서버는 여전히 완전히 사용자 머신에서 실행됩니다.

두 방법과 토큰 수명에 대해서는 인증을 참조하세요.


작동 방식

모든 것이 하나의 Docker 컨테이너에서 실행되며 디스크의 .md 파일과 직접 작업합니다:

  • 볼트가 진실의 원천으로 유지됩니다 — 서버는 Obsidian 앱이 사용하는 것과 동일한 일반 Markdown 파일을 읽고 씁니다.

  • 검색은 파생 데이터입니다 — 파일 감시자가 노트가 변경됨에 따라 인덱스(키워드 + 벡터)를 최신 상태로 유지하며, 언제든지 노트에서 재구축할 수 있습니다.

  • 원격 이미지는 동기화 루프를 추가합니다 — 번들된 Obsidian Sync 서비스가 컨테이너의 볼트를 모든 기기와 최신 상태로 유지합니다: 휴대폰에서 노트를 편집하면 잠시 후 검색 가능해지고, 에이전트가 노트를 작성하면 Obsidian에 표시됩니다.

graph LR
    subgraph container ["One Docker container"]
        Sync["sync service<br/>(remote image)"]
        Vault[("/vault<br/>.md files — source of truth")]
        Index[("search index<br/>keywords + vectors")]
        Server["MCP server"]
        Sync <-->|read/write| Vault
        Vault -->|file watcher| Index
        Server <-->|read/write| Vault
        Server -->|query| Index
    end
    Obsidian["Your Obsidian apps<br/>(phone, laptop)"] <-->|Obsidian Sync| Sync
    Client["Any MCP client<br/>(Claude, Cursor, claude.ai)"] -->|OAuth 2.1 / Bearer| Server

전체 설계, 인증 흐름 다이어그램 및 구성 요소 분석은 ARCHITECTURE.md를 참조하세요.


하이브리드 검색

키워드 검색만으로는 사용자의 어휘가 볼트의 어휘와 일치하지 않을 때 실패합니다 — "aspirations"는 "targets"에 대한 노트를 찾지 못하고, "coworkers"는 "references" 파일을 표시하지 않습니다. 실제 볼트를 대상으로 한 테스트에서 자연어 쿼리의 30%가 키워드만으로는 결과가 없거나 관련성이 떨어지는 결과를 반환했습니다. 하이브리드 검색은 이러한 누락을 제거했습니다 — 벡터가 어휘 격차를 메우고, 리랭커가 어느 신호도 단독으로 강하지 않은 의도 중심 쿼리를 구출합니다.

하이브리드 검색은 Reciprocal Rank Fusion을 통해 세 가지 순위 신호를 결합합니다:

  • 키워드 (FTS5)는 정확한 용어, 전문 용어 및 속성 값에 대해 정밀도를 유지합니다

  • 벡터 (sqlite-vec)는 의미 기반 매칭으로 어휘 격차를 메웁니다

  • 리랭커 (cross-encoder)는 각 쿼리-문서 쌍을 공동으로 점수화하여 순서를 개선합니다 — 키워드와 벡터가 모두 놓치는 의도 중심 쿼리를 구출합니다

모든 모델은 로컬에서 실행됩니다(총 약 45MB, 외부 API 없음). 키워드 전용 검색은 EMBEDDING_ENABLED=false로 설정하고, 낮은 지연 시간을 위해 리랭킹을 건너뛰려면 RERANK_MODE=none으로 설정하세요.

모델 세부 정보, 혼합 가중치 및 전체 파이프라인 분석은 ARCHITECTURE.md → 하이브리드 검색을 참조하세요.


메모리

계속 성장하는 메모리 레이어는 에이전트가 모든 것을 컨텍스트에 덤프하지 않고 올바른 항목을 검색할 수 있을 때만 유용합니다. 여러 파일에 걸쳐 수백 개의 날짜가 있는 항목(선호도, 원칙, 커뮤니케이션 스타일, 진행 중인 약속)이 있으면 전체 파일을 읽는 것은 관련 없는 자료로 컨텍스트를 낭비하고 신호를 묻어버립니다. 메모리 시스템은 대상 검색을 위해 설계되었습니다: 에이전트는 시간이 지남에 따라 지식을 축적하고 현재 작업에 정확히 관련된 내용만 회상합니다.

이 레이어는 주제 제목 아래에 날짜가 있는 항목을 보관하는 일반 Markdown 파일 폴더(기본값: About Me/)입니다 — 첫 실행 시 시작 템플릿으로 자동 생성되며, 에이전트가 vault_update_memory를 통해 성장시킵니다. 세 가지 속성이 작동하게 합니다:

  • 추가 전용 — 항목은 절대 덮어쓰지 않습니다. 수정 사항은 새 날짜 항목으로 도착합니다. 이 레이어는 현재 상태 와 그 뒤의 진화 과정을 포착하는 개인 지식 기반이 됩니다

  • 주제 회상 — vault_memory_recall은 모든 메모리 파일에서 관련 항목을 한 번에 검색하며, 키워드 및 의미 기반 매칭으로 가장 오래된 것부터 반환합니다. "X에 대해 어떻게 생각하나요?"라고 물으면 현재 견해와 발전 과정의 날짜별 이력을 얻을 수 있습니다 — 전체 파일을 읽거나 어떤 파일에 무엇이 있는지 추측할 필요가 없습니다

  • 저하 없이 성장 — 결과 제한(max_results)은 가장 관련성이 낮은 항목을 제거하며, 타임라인의 일부를 잘라내지 않습니다. 500개 항목이 있는 메모리 레이어는 50개 항목이 있는 레이어만큼 잘 대상 쿼리를 처리합니다

현재 상태가 아니라 현재의 것을 설명하는 파일(루틴, 활성 약속)은 frontmatter에서 entry-policy: living을 선언할 수 있습니다 — 만료된 항목은 보존되지 않고 정리 가능하여 현재 상태 그림을 정확하게 유지합니다.

전체 레이어는 선택 사항입니다 — MEMORY_ENABLED=false로 설정하면 메모리 도구를 숨기고 폴더 자동 생성을 완전히 건너뜁니다.

회상 파이프라인, 인덱싱 모델, 자동 초기화 및 옵트아웃 동작은 ARCHITECTURE.md → 메모리를 참조하고, 파일 형식, entry-policy 규칙 및 시작 템플릿은 templates/memory를 참조하세요.


작업

작업 메타데이터는 일반 마크다운에 있습니다 — 파일 전체에 흩어져 있고, 이모지 표시 또는 인라인 필드로 인코딩되며, Kanban 제목 아래에 구성됩니다. "기한이 지난 것은 무엇인가요?"에 답하는 에이전트는 모든 파일을 파싱하고 선택한 형식을 이해해야 합니다. Kanban 보드에서 작업을 완료하려면 보드의 레인 구조, 날짜 구문 및 완료 레인이 어느 제목인지 알아야 합니다.

작업 레이어는 에이전트가 그렇게 할 필요가 없도록 처리합니다:

  • 찾기 — 상태, 6개의 날짜 필드(마감, 예약, 시작, 생성, 완료, 취소), 우선순위, 폴더 또는 Kanban 레인으로 필터링합니다. 각 결과는 레인, 노트 경로, 제목 및 줄 번호를 전달합니다 — 작업을 찾기 위해 후속 읽기가 필요 없습니다

  • 업데이트 — 단일 호출로 완료, 우선순위 변경 및 Kanban 레인 간 작업 이동을 수행합니다. 작업을 완료로 표시하면 완료 레인을 자동 감지하고 완료 날짜를 기록합니다. 되돌리면 날짜가 제거됩니다. 세 가지 변경이 모두 동시에 발생할 수 있습니다

  • 두 형식 모두 — Tasks 플러그인 이모지 표시 또는 Dataview 인라인 필드 중 어떤 형식을 사용하든, 서버는 둘 다 읽고 Tasks 플러그인이 구성된 형식으로 씁니다

인덱싱 모델, 날짜 계단식 정렬 및 Kanban 레인 감지는 ARCHITECTURE.md → 작업을 참조하세요.


파일

노트에는 스크린샷, 참조 아키텍처 다이어그램이 포함되고 캔버스 및 데이터 파일에 링크됩니다 — 그러나 마크다운을 읽는 에이전트에게 ![[diagram.png]]는 단지 텍스트일 뿐입니다. vault-cortex는 파일을 볼트 주변의 잡동사니가 아닌 볼트의 일부로 취급합니다 — 링크되고, 크기가 조정되며, 에이전트가 실제로 사용할 수 있는 형태로 읽을 수 있습니다:

  • 이미지 — 파일 이름이 아닌 이미지 자체입니다. 스크린샷과 다이어그램은 MCP 클라이언트가 수용하는 크기를 초과할 때 서버 측에서 축소 및 재압축되므로, 휴대폰 세션에서도 5MB 아키텍처 다이어그램을 볼 수 있습니다

  • 캔버스 — Canvas 보드가 읽을 수 있는 개요로 도착합니다: 그룹, 각 카드의 내용(읽기 순서), 그리고 그 사이의 연결입니다. 캔버스 콘텐츠는 전체 텍스트 검색이 가능하며, 보드의 파일 참조는 링크 그래프에 나타납니다 — 역링크와 외부 링크는 노트 간 링크와 동일하게 작동합니다. 완전한 충실도가 필요할 때 정확한 JSON 소스는 플래그 하나로 접근할 수 있습니다

  • PDF — 텍스트는 제목 계층, 코드 블록 및 하이퍼링크가 보존된 채 추출됩니다. PDF 콘텐츠는 노트와 함께 전체 텍스트 검색이 가능합니다. raw: true로 설정하면 페이지를 이미지로 렌더링하여 텍스트 추출이 보존할 수 없는 레이아웃, 다이어그램 및 표를 표시합니다 — 스캔 및 이미지 전용 PDF는 이 모드에서 작동합니다

  • 텍스트 및 데이터 파일 — TXT, SVG, JSON, XML, CSV, YAML, 로그 및 Bases 파일은 작성된 그대로 반환됩니다. 처음 100KB의 콘텐츠는 전체 텍스트 검색이 가능합니다. 큰 데이터 파일과 로그는 한 번에 한 줄 범위씩 읽을 수 있으며, 각 페이지는 현재 위치와 남은 파일 크기를 보고합니다

  • 찾아보기 — 표시된 폴더의 파일을 확장자별 개수와 파일 크기와 함께 나열합니다. 노트가 링크하는 파일은 링크 그래프에서도 크기를 보고합니다

FILE_TOOLS_ENABLED=false로 설정하면 파일 도구를 숨길 수 있습니다 — 원격 볼트가 첨부 파일 없이 동기화될 때 유용합니다.

이미지 파이프라인 및 디스패치 모델은 ARCHITECTURE.md → 파일을 참조하세요.


도구

카테고리

도구

설명

볼트 CRUD

vault_read_note

노트 읽기 — 전체 본문, 속성, 개요 또는 섹션

vault_write_note

노트 생성 (이미 존재하면 실패, overwrite 설정 시 교체)

vault_patch_note

제목 대상 편집 (include_children 가드와 함께 추가, 앞에 삽입, 교체, 삽입)

vault_replace_in_note

노트에서 텍스트 찾아 바꾸기 (첫 번째 일치 또는 replace_all_occurrences)

vault_delete_span

짧은 앵커로 줄 블록 삭제, 전체 재인용 불필요

vault_list_notes

선택적 glob/폴더 필터로 노트 나열

vault_delete_note

노트 삭제 (보호 경로 적용)

vault_move_note

노트 이동 또는 이름 변경, 볼트 전체의 링크 재작성

검색

vault_search

태그/폴더/속성/날짜 필터가 있는 하이브리드 검색

vault_search_by_tag

태그로 노트 찾기 (정확히 일치 또는 접두사 일치)

vault_search_by_folder

메타데이터와 함께 폴더의 노트 탐색

vault_recent_notes

최근 수정 또는 생성된 노트

vault_list_tags

사용 횟수가 포함된 모든 태그

작업

vault_list_tasks

볼트 전체 작업 인덱스 — 칸반 인식, 6개 날짜 필드, 우선순위, 폴더/제목 범위

vault_update_task

한 번의 호출로 상태, 우선순위, 레인 변경 — 칸반 보드의 완료 레인 자동 감지

메모리

vault_get_memory

구조화된 메모리 읽기 (파일, 섹션 또는 전체)

vault_update_memory

메모리 섹션에 날짜가 있는 항목 추가

vault_delete_memory

날짜별로 특정 메모리 항목 제거

vault_list_memory_files

메모리 파일, 해당 섹션 및 각 파일의 항목 정책 검색

vault_memory_recall

메모리 파일 전체에서 주제에 대한 항목 단위 하이브리드 회상, 오래된 것부터

속성

vault_list_property_keys

샘플 값이 포함된 모든 속성 키

vault_list_property_values

속성 키에 대한 고유 값

vault_search_by_property

속성 키-값으로 노트 찾기

vault_update_properties

본문을 건드리지 않고 속성 추가 또는 업데이트

링크

vault_get_backlinks

주어진 경로를 가리키는 노트

vault_get_outgoing_links

주어진 노트에서 나가는 링크

vault_find_orphans

들어오는 링크가 없는 노트

파일

vault_read_file

마크다운이 아닌 파일 읽기 — 이미지는 이미지로, 캔버스는 읽을 수 있는 개요로 제공

vault_list_files

크기 및 확장자별 개수와 함께 볼트의 마크다운이 아닌 파일 탐색

데일리 노트

vault_get_daily_note

오늘 (또는 특정 날짜) 데일리 노트


프롬프트

도구는 모델 기반입니다 — 어시스턴트가 호출합니다. 프롬프트는 사용자가 트리거하는 워크플로우입니다. 각 프롬프트는 호출 시점에 검색 인덱스, 링크 그래프 및 메모리 레이어를 쿼리한 다음 안내 지침과 함께 결과를 조합합니다 — 따라서 세션이 가정이 아닌 볼트의 실제 상태에 기반하여 시작됩니다.

프롬프트

인수

기능

vault-orientation

—

볼트 통계, 폴더 분포, 속성 채택률 (낮은 채택 플래그), 고아 노트, 끊어진 링크 수, 태그, 최근 노트 및 메모리 레이어를 조사 — 상황별 도구 제안 포함

memory-review

file?, max_chars?

구조적 개요 (범위 콜아웃, 섹션 항목 수) + 날짜가 있는 콘텐츠를 타임라인으로. 안내 성찰: 진화 서사, 범위 적합성, 백필 갭, 적용 범위 분석 — 기본적으로 추가 전용, 정리 제안은 entry-policy: living 파일에만. MEMORY_ENABLED=false, READONLY_MODE=true 또는 DISABLED_TOOLS에 vault_update_memory가 포함된 경우 숨겨짐.

daily-review

date?, max_chars?

하루 조정 — 데일리 노트, 볼트 전체 작업 상태 (기한/기한 초과, 예약됨), 수정된 노트, 나가는 링크 (끊어진 링크 감지) 및 역링크 — 무슨 일이 있었는지, 무엇이 열려 있는지, 무엇이 후속 조치가 필요한지 표면화

프롬프트는 구성 (MEMORY_DIR, 데일리 노트 설정)에 맞게 조정되며 모든 볼트에서 즉시 작동합니다. 클라이언트에 페이로드 제한이 있는 경우 max_chars를 전달하여 포함된 콘텐츠를 제한하세요.

클라이언트 지원: 프롬프트는 Claude Desktop (Chat 및 Cowork — 커넥터 아래 + 메뉴), Claude Code (슬래시 명령) 및 OpenCode에서 작동합니다. 다른 클라이언트 (Cursor, Windsurf)의 지원은 다양합니다 — 최신 정보는 MCP 클라이언트 매트릭스를 참조하세요.


속성

Vault Cortex는 노트의 모든 속성을 인덱싱하지만, 5개는 승격된 처리를 받습니다 — 빠른 필터링을 위한 전용 열과 모든 검색 및 검색 결과의 최상위 필드:

속성

할 수 있는 작업

title

검색 결과의 표시 이름, 없으면 파일 이름으로 대체

tags

태그로 검색 및 필터링, 부모-자식 계층 포함 (project는 project/vault-cortex와 일치)

type

노트 유형별 필터링 — meeting, person, session-log 또는 볼트에서 사용하는 모든 값

created

생성 날짜로 정렬하고 각 검색 결과와 함께 각 노트가 생성된 시점 확인

related

특정 링크를 상호 참조하는 노트 필터링 — 그래프 쿼리 없이는 보이지 않는 연결 표면화

다른 모든 속성도 완전히 쿼리 가능합니다 — 결합된 텍스트 + 메타데이터 쿼리에는 filters.properties와 함께 vault_search를 사용하거나, 메타데이터 전용 조회에는 vault_search_by_property를 사용하세요. vault_list_property_keys 및 vault_list_property_values는 볼트 전체에 존재하는 속성을 검색합니다.

이것들은 요구 사항이 아닌 규칙입니다 — Vault Cortex는 모든 속성 스키마에서 작동합니다. 승격된 속성은 기본적으로 더 풍부한 필터링과 더 깔끔한 결과를 제공할 뿐입니다.

선행 콜아웃도 동일한 처리를 받습니다. 노트의 첫 번째 본문 콘텐츠가 Obsidian 콜아웃 (> [!type])인 경우 — 프론트매터 바로 뒤 또는 제목 바로 뒤 — 인덱싱되어 모든 검색 결과와 함께 표시됩니다 (vault_search에서 include_leading_callout으로 요청). 이렇게 하면 노트가 자체 설명적이 됩니다: 결과를 스캔하는 에이전트는 읽을 노트를 결정하기 전에 각 노트가 무엇을 위한 것인지 볼 수 있습니다. 메모리 템플릿은 이를 위해 > [!info] Scope of this file 콜아웃을 사용하며, 볼트의 모든 노트는 동일한 패턴을 사용할 수 있습니다.


구성

모든 설정은 합리적인 기본값이 있는 환경 변수입니다. 원격 배포는 Obsidian Sync 자체 설정도 전달합니다 — DEVICE_NAME, SYNC_MODE, CONFLICT_STRATEGY, SYNC_CONFIGS, SYNC_EXCLUDED_FOLDERS, SYNC_FILE_TYPES — 원격 가이드의 구성 표에 문서화되어 있습니다.

변수

필수?

기본값

설명

MCP_AUTH_TOKEN

예

—

인증을 위한 Bearer 토큰 (JWT 서명 키이기도 함)

VAULT_PATH

로컬 전용

—

볼트의 호스트 경로 (바인드 마운트 소스, 원격은 명명된 볼륨 사용)

PUBLIC_URL

원격 전용

—

OAuth 검색 메타데이터용 공개 URL. 설정하지 않으면 Render와 Railway에서 자동으로 채워짐 (RENDER_EXTERNAL_URL 또는 RAILWAY_PUBLIC_DOMAIN에서)

OBSIDIAN_AUTH_TOKEN

원격 전용

—

Obsidian Sync 인증 토큰 — CLI의 get-sync-token이 자동으로 캡처해 줌

VAULT_NAME

원격 전용

—

Obsidian Sync 볼트의 정확한 이름 (대소문자 구분)

VAULT_PASSWORD

원격 전용

—

볼트에 설정된 경우 종단 간 암호화 비밀번호. 그 외에는 비워 둠.

STORAGE_ROOT

—

—

단일 영구 볼륨을 허용하는 컨테이너 호스팅 플랫폼(Railway, Render)에서 유지해야 하는 모든 것(볼트, 검색 인덱스, Obsidian Sync 상태)을 위한 단일 디렉터리. 볼륨을 해당 경로에 마운트하고 이 값을 같은 경로로 설정

EMBEDDING_ENABLED

—

true

false로 설정하면 임베딩 파이프라인을 비활성화 — 모델 다운로드, 벡터 테이블, 임베딩 패스, 하이브리드 검색을 건너뜀. 검색은 FTS5 키워드 매칭으로 대체됨.

RERANK_MODE

—

blended

크로스 인코더 재랭킹 모드: blended는 RRF 융합 후 위치 인식 점수 블렌딩을 적용(~200ms 지연 추가), none은 재랭킹을 건너뜀. EMBEDDING_ENABLED가 true일 때만 적용됨.

MEMORY_ENABLED

—

true

false로 설정하면 메모리 레이어를 완전히 비활성화 — 메모리 도구를 숨기고, 부트스트랩을 건너뛰며, 서버 메타데이터에서 메모리를 제외함. false일 때 MEMORY_DIR은 무시됨.

FILE_TOOLS_ENABLED

—

true

false로 설정하면 파일 도구(vault_read_file, vault_list_files)를 숨김 — Obsidian Sync에서 첨부 파일 동기화가 비활성화된 원격 배포에 유용함.

READONLY_MODE

—

false

true로 설정하면 볼트를 변경하는 모든 도구를 숨기고 메모리 폴더 자동 생성을 건너뜀 — 연결된 클라이언트는 읽기와 검색만 가능하고 편집은 불가능함.

DISABLED_TOOLS

—

—

개별 도구를 이름으로 숨김, 쉼표로 구분 (예: vault_delete_note,vault_move_note). 이름은 도구 표의 Name 열과 일치함. 차감 방식만 지원 — 다른 설정이 숨긴 도구를 다시 활성화할 수 없음. 알 수 없는 도구 이름은 시작 시 서버를 중지시키므로 오타가 즉시 드러남.

MEMORY_DIR

—

About Me

구조화된 메모리 파일을 위한 볼트 폴더

PROTECTED_PATHS

—

MEMORY_DIR, DAILY_NOTES_FOLDER

vault_delete_note가 삭제를 거부하는 폴더

ORPHAN_EXCLUDE_FOLDERS

—

DAILY_NOTES_FOLDER, Templates, MEMORY_DIR

고아 파일 감지에서 제외되는 폴더

DAILY_NOTES_FOLDER

—

볼트 구성에서 읽음

데일리 노트가 저장되는 폴더를 설정. 설정하지 않으면 볼트의 .obsidian/daily-notes.json에서 읽고, 없으면 Daily Notes로 대체됨. 데일리 노트 참조.

DAILY_NOTES_FORMAT

—

볼트 구성에서 읽음

데일리 노트 파일명 형식을 설정 — Obsidian의 데일리 노트 날짜 형식 설정과 동일한 토큰 사용. 설정하지 않으면 볼트의 .obsidian/daily-notes.json에서 읽고, 없으면 YYYY-MM-DD로 대체됨. 데일리 노트 참조.

TZ

—

UTC

타임스탬프와 데일리 노트 결정에 사용되는 IANA 시간대

SERVICE_DOCUMENTATION_URL

—

GitHub 저장소 URL

OAuth 검색 메타데이터에서 반환되는 URL

LOG_LEVEL

—

info

로깅 상세 수준: debug, info, warn, error

LOG_DIR

—

/data/logs (원격), $STORAGE_ROOT/data/logs (단일 볼륨), none (로컬)

컨테이너 재생성 후에도 유지되는 로그 파일 디렉터리. 컨테이너 자체 로그(docker logs에 표시되는 것)는 항상 기록되지만, Docker는 이미지 업데이트나 구성 변경 시 컨테이너가 재생성될 때마다 이를 폐기함. LOG_DIR 아래의 날짜 스탬프 파일은 데이터 볼륨에 저장되어 유지됨. none은 컨테이너 로그만 유지함.

LOG_RETENTION_DAYS

—

90

시작 시 자동 정리 전 로그 파일을 보관하는 일수; LOG_DIR이 경로일 때만 적용됨

WINDOWS_MODE

—

false

Windows를 사용 중인가? true로 설정. 파일 감시자를 폴링으로 전환하고 노트 이동을 이름 변경 기반 쓰기로 전환하여 C: 드라이브의 볼트가 Docker Desktop에서 작동하도록 함. 모든 Windows 환경에서 켜두어도 안전하며, macOS/Linux/WSL2에서는 불필요함.

MAX_FILE_BYTES

—

52428800 (50 MiB)

vault_read_file이 읽을 최대 파일 크기(바이트). 이 값을 초과하는 파일은 읽기 전에 거부됨. 개별 파일이 매우 큰 볼트에서는 값을 높일 것.

MAX_IMAGE_OUTPUT_BYTES

—

49152 (48 KiB)

vault_read_file이 전달하는 이미지의 바이트 예산, base64 인코딩 전 바이너리 바이트 기준. 이 값을 초과하는 이미지는 맞게 축소 및 재압축됨. 가장 제한적인 주류 MCP 클라이언트 한도에 맞게 설정됨; 더 큰 응답을 허용하는 클라이언트에서는 값을 높일 것.

MAX_PDF_RENDER_PAGES

—

5

vault_read_file에 raw: true가 설정된 경우 이미지로 렌더링할 최대 PDF 페이지 수. 페이지당 바이트 예산은 MAX_IMAGE_OUTPUT_BYTES를 렌더링된 페이지 수로 균등하게 나눈 값 — 페이지가 적을수록 각 페이지의 품질이 높아짐.

TRUST_PROXY_HOPS

—

0

X-Forwarded-For에서 클라이언트 IP를 파생하는 데 사용되는 신뢰할 수 있는 리버스 프록시 홉 수 (OAuth 속도 제한, 요청 로그). 서버 앞에 제어하는 프록시가 정확히 하나 있을 때(Caddy, nginx, Cloudflare Tunnel, API Gateway) 1로 설정. 0이면 주입된 전달 헤더가 무시됨.

TRUST_FORWARDED_HOPS

—

0

RFC 7239 Forwarded 헤더의 끝에 있는 for= 항목 중 제어하는 프록시에 속하는 항목 수. 0은 헤더를 무시함; 앞의 프록시가 이를 작성할 때 1 (예: AWS API Gateway); CDN이 해당 프록시 앞에 있고 유일한 접근 경로일 때 2.

  • 스마트 기본값 — MEMORY_DIR 또는 DAILY_NOTES_FOLDER를 설정하면 PROTECTED_PATHS와 ORPHAN_EXCLUDE_FOLDERS의 기본값이 자동으로 업데이트됩니다. DAILY_NOTES_FOLDER가 설정되지 않으면 Daily Notes가 그 자리를 채웁니다. daily-notes.json에만 구성된 일일 노트 폴더는 인식되지 않습니다 — 직접 PROTECTED_PATHS에 추가하세요. 완전히 사용자 정의 목록을 위해서만 명시적으로 설정하면 됩니다.

  • MEMORY_ENABLED=false — 메모리 계층을 완전히 비활성화합니다. 메모리 도구가 숨겨지고 메모리 폴더가 자동 생성되지 않습니다.

  • FILE_TOOLS_ENABLED=false — 파일 도구를 완전히 숨깁니다. Obsidian Sync에서 첨부 파일 동기화가 비활성화되어 디스크에 파일이 없을 때 유용합니다.

  • READONLY_MODE=true — 볼트에 쓰는 모든 도구를 숨기고 메모리 폴더 자동 생성을 건너뜁니다. 연결된 클라이언트는 읽고 검색만 할 수 있으며 편집은 불가능합니다.

  • DISABLED_TOOLS — 지정한 도구만 정확히 숨깁니다. 위의 스위치보다 세밀한 제어가 필요할 때 사용합니다. 예: 쓰기는 유지하되 vault_delete_note와 vault_move_note를 제거. 도구 설명과 프롬프트의 가용성 기반 상호 참조는 자동으로 조정됩니다.

메모리 파일 예시와 날짜 기반 항목 설계 철학은 templates/memory/를 참조하세요.

일일 노트

vault_get_daily_note와 일일 검토 프롬프트는 Obsidian에서 구성한 폴더 및 파일 이름 날짜 형식을 사용하여 일일 노트를 찾습니다. 이 설정은 볼트의 .obsidian/daily-notes.json에서 읽습니다:

  • 로컬 모드 — 바인드 마운트된 볼트에서 파일을 직접 읽습니다. 설정할 것이 없습니다.

  • 원격 모드 — Obsidian Sync의 볼트 구성 동기화를 통해 받습니다. 서버는 기본적으로 이를 가져옵니다(.env의 SYNC_CONFIGS 설정). 하지만 푸시 측을 활성화해야 할 가능성이 높습니다: 기기별로 Obsidian 설정 → 동기화 → 볼트 구성 동기화. 자세한 내용은 원격 가이드의 Daily notes 섹션을 참조하세요.

파일을 사용할 수 없거나 — 설정을 반영하지 않는 Periodic Notes 플러그인을 사용하는 경우 — DAILY_NOTES_FOLDER(볼트 상대 경로: Journal, Planner/Daily)와 DAILY_NOTES_FORMAT(Obsidian의 날짜 형식 설정과 동일한 토큰: YYYY-MM-DD-dddd, YYYY/MM/DD, MMM D, YYYY, …)을 설정하세요. 하나 또는 둘 다 설정할 수 있습니다 — 설정된 값은 항상 구성 파일보다 우선합니다. 두 소스가 모두 없으면 서버는 Daily Notes와 YYYY-MM-DD로 대체합니다.

참고: 일부 날짜 형식 토큰은 지원되지 않습니다 — 서수(Do, Mo, DDDo, wo), dd(2자리 요일), d(요일 번호), e, k/kk, 그리고 지역화된 형식(L–LLLL, LT, LTS). 서버는 이러한 토큰으로 Obsidian이 생성하는 파일 이름을 재현할 수 없으므로 노트를 찾을 수 없습니다. 형식에 이러한 토큰이 포함된 경우 vault_get_daily_note는 명확한 오류를 반환합니다 — Obsidian에서 형식을 변경하거나 DAILY_NOTES_FORMAT을 지원되는 대안으로 설정하세요.


데이터 무결성

Vault Cortex는 개인 노트에 씁니다 — 파일 안전 계층은 오류뿐만 아니라 손상을 방지하도록 설계되었습니다.

  • 원자적 쓰기 — 모든 파일 쓰기는 임시 파일에 스테이징한 후 이름을 바꿉니다. 독자는 부분적이거나 0바이트인 노트를 볼 수 없습니다. 독점 생성은 link()(POSIX no-clobber)를 사용하여 노트 이동 시 TOCTOU 창을 닫습니다.

  • 파일별 뮤텍스 — 동시 MCP 도구 호출은 파일별로 직렬화되거나 빠르게 실패합니다. 이동은 소스, 대상, 모든 백링크 소스를 하나의 단위로 잠급니다.

  • 경로 탐색 차단 — resolveSafePath()는 모든 경로를 확인한 후 접두사 검사를 수행합니다. 보호된 경로 삭제는 정규화 후 거부됩니다. 메모리 파일 이름은 경계에서 구분 기호를 거부합니다.

  • 숨김 경로는 접근 불가 — 점으로 시작하는 파일과 폴더(.obsidian/, .trash/)는 목록이나 검색에 표시되지 않으며, 직접 대상으로 하는 도구 호출은 Obsidian과 일치하게 거부됩니다. 플러그인 구성 및 API 키는 접근할 수 없습니다.

  • 주입 방지 — 검색 쿼리는 매개변수화되고 FTS5로 정화됩니다. 프롬프트 콘텐츠는 XML 데이터 마커로 감싸고 닫는 태그 이스케이프를 사용하여 태그 탈출 주입을 방지합니다.

  • 컨테이너 강화 — 비루트 사용자, PID 1 init, 런타임 이미지에 패키지 관리자 없음, 다이제스트 고정 기본 이미지, 정상 종료.

메커니즘 세부 사항은 ARCHITECTURE.md → 데이터 무결성을, 서버의 각 부분이 어떻게 강화되는지는 SECURITY.md → 런타임 강화를 참조하세요.


인증

개인 노트에 대한 읽기/쓰기 액세스 권한이 있는 서버의 경우 인증은 선택 사항이 아닙니다. Vault Cortex는 PKCE 및 리프레시 토큰 회전을 포함한 전체 OAuth 2.1 사양을 구현합니다. AWS (SST) 배포는 심층 방어를 추가합니다: 요청은 두 개의 독립적인 계층(API Gateway Lambda authorizer + Express 미들웨어)에서 검증됩니다. BlueRock의 2026 MCP 보안 분석에 따르면 MCP 서버 중 8.5%만 OAuth를 구현하고 41%는 인증이 전혀 없습니다.

두 가지 방법:

방법

사용처

토큰 형식

OAuth 2.1

Claude Desktop, Claude Code, claude.ai, 모든 OAuth 클라이언트

JWT (HS256, 24h)

정적 베어러

Claude Code, MCP Inspector, curl

원시 MCP_AUTH_TOKEN

OAuth는 동적 클라이언트 등록을 사용합니다 — Client ID/Secret이 필요 없습니다. 브라우저에서 동의 페이지가 열립니다. MCP_AUTH_TOKEN을 입력하여 승인하세요. 리프레시 토큰은 60일 슬라이딩 만료 기간이 있습니다(일일 사용자는 다시 인증할 필요가 없습니다). MCP_AUTH_TOKEN을 회전하면 모든 세션이 종료됩니다 — 각 클라이언트는 동의 페이지를 통해 다시 권한을 부여받습니다.

전체 흐름 다이어그램은 ARCHITECTURE.md → 인증을 참조하세요.


배포 옵션

로컬 실행은 사용자 머신에서 실행됩니다. 원격 배포는 VPS 또는 호스팅 컨테이너 플랫폼에서 실행됩니다 — 노트북이 닫혀 있어도 볼트에 접근할 수 있습니다.

어떤 경로를 선택하든 서버는 교체 가능하고 볼트는 그렇지 않습니다. 노트는 일반 Markdown 파일이며 Obsidian이 모든 기기에 동기화합니다. 컨테이너는 사본과 처음부터 다시 빌드할 수 있는 인덱스를 보유합니다. VPS를 종료하고 Render 또는 Railway 서비스를 삭제하고 호스트를 전환해도 — 동일한 파일이 여전히 사용자 머신과 Obsidian Sync에 있으며 어떤 것으로도 읽을 수 있습니다. 이것이 실제 홈이 공급업체 데이터베이스인 AI 노트북과의 차이점입니다: 여기서 호스트는 편의 시설이지 관리인이 아닙니다.

경로

무엇

가이드

로컬

사용자 머신의 볼트 — 무료, 클라우드 없음

deploy/local/

원격 · 원클릭

Render 또는 Railway — 영구 볼륨 1개, 관리할 서버 없음

deploy/render/ · deploy/railway/

원격 · 자체 호스팅

VPS + Obsidian Sync — 모든 기기에서 접근

deploy/remote/

원격 · AWS (SST)

IaC 참조 배포 — 자동화된 인프라, 심층 방어 인증

DEPLOY.md

AWS 경로에는 이 저장소를 위해 구축된 CI/CD 워크플로가 포함됩니다 — 포크 사용자는 배포 전에 자체 자격 증명과 스테이지를 구성해야 합니다.

모든 경로는 동일한 이미지 ghcr.io/aliasunder/vault-cortex를 실행합니다 — :latest는 MCP 서버 단독(로컬), :remote는 s6-overlay 감독 하에 동일한 컨테이너에 Obsidian Sync를 번들합니다(원클릭, 자체 호스팅, AWS). 하나의 컨테이너는 모든 OCI 런타임에서 작동합니다: docker run, Podman, nerdctl — Docker Compose는 선택 사항입니다.

Docker Hub에도 있습니다: 동일한 이미지가 aliasunder/vault-cortex에 미러링됩니다. GHCR이 기본 소스이고 Hub 태그는 동일합니다.

비용: 원격 설정에는 VPS 또는 호스팅 플랫폼 요금제와 Obsidian Sync 월 $4 USD가 필요합니다. 2 GiB 인스턴스는 일반적인 볼트의 의미론적 검색에 충분합니다. 4 GiB는 동시 검색 및 더 큰 볼트를 위한 여유를 추가합니다. 의미론적 검색을 완전히 건너뛰면 더 작게 유지할 수 있습니다. 로컬 전용은 무료입니다. 참조 AWS 배포는 월 약 $17–29 USD의 총 비용이 듭니다.

원클릭 배포

버튼과 사전 요구 사항은 빠른 시작 → 원격에 있습니다. 각 가이드는 배포, URL 및 토큰을 찾는 방법, 업데이트 방법, 삭제 방법을 안내합니다: deploy/render/ (저장소 루트의 render.yaml Blueprint에서) · deploy/railway/ (게시된 템플릿에서).

커뮤니티 배포

커뮤니티에서 구축하고 유지 관리하는 배포 템플릿 — 여기서 테스트되지 않았으며 릴리스보다 뒤처질 수 있습니다.

  • vault-cortex-aca — @flytzen의 Azure Container Apps용 Bicep 템플릿. Container Apps 수신 뒤에서 :remote 이미지를 무료 관리 HTTPS로 실행합니다. 스토리지는 의도적으로 임시이며 Obsidian Sync가 진실의 원천입니다.

다른 플랫폼용 배포를 구축하셨나요? 여기에 추가하려면 PR을 열어주세요.


개발

# Run locally with hot reload
PUBLIC_URL=http://localhost:8000 MCP_AUTH_TOKEN=local-dev-token VAULT_PATH=~/Vault npm run dev:mcp

# Tests
npm test

# Full check suite
npm run prettier:check && npm run lint && npm test && npm run build

npm test에는 실제 서버를 부팅하고 모든 도구와 프롬프트를 HTTP로 호출하는 통합 테스트가 포함됩니다 — 인증 강제, 구성 게이트 도구 표면, 쓰기 변형 무결성(각 쓰기는 다시 읽음), 잘못된 구성 시 부팅 거부를 검증합니다. 보안 관련 범위는 SECURITY.md를 참조하세요.

MCP Inspector — 도구 테스트를 위한 대화형 브라우저 UI:

# Start server (terminal 1), then:
npx @modelcontextprotocol/inspector
# Enter http://localhost:8000/mcp as URL, local-dev-token as Bearer token

전체 개발 설정은 CONTRIBUTING.md를 참조하세요.


동반: obsidian-vault 스킬

MCP 서버는 모든 클라이언트와 독립적으로 작동합니다. skills을 지원하는 에이전트(Claude Code, Cursor, Windsurf, Cline 및 70개 이상)의 경우 obsidian-vault 스킬은 Obsidian 스타일 마크다운에 대한 더 깊은 지식을 추가합니다 — frontmatter 규칙, callout 구문, Dataview, Tasks, Kanban과 같은 플러그인별 형식.

npx skills add aliasunder/agent-skills --skill obsidian-vault

스킬 소스 →


로드맵

단계

무엇

상태

1

볼트 CRUD, 전체 텍스트 검색(FTS5), 메모리 계층, OAuth 2.1

완료

2a

하이브리드 검색 — FTS5 + 벡터 + RRF 융합, 헤딩 인식 청킹

완료

2b

리랭커 — 교차 인코더 리랭킹, 위치 인식 점수 혼합

완료

3a

작업 계층 — 볼트 전체 작업 인덱스, 구조화된 쿼리, 원콜 작업 업데이트(Tasks 플러그인 이모지 + Dataview 형식)

완료

3b

메모리 회상 — 메모리 계층의 날짜 기반 기록에 대한 항목 수준 검색

완료

3c

그래프 쿼리 — 볼트의 기존 wikilink 그래프에 대한 다중 홉 탐색(경로, 이웃)

탐색 중


감사의 말

Obsidian sync는 obsidian-headless로 구동됩니다. 이는 @Belphemur의 obsidian-headless-sync-docker에서 영감을 받은 컨테이너화 방식입니다. :remote 이미지의 s6-overlay 감독 스캐폴딩은 해당 프로젝트의 유지 관리 포크에서 가져와 현재 이 저장소에 있습니다.

하이브리드 검색 파이프라인은 @tobi의 qmd에서 패턴을 차용했습니다 — 순위 보너스가 적용된 RRF 융합, 크로스 인코더 재순위화를 위한 위치 인식 점수 혼합, 콘텐츠 해시 게이팅, 그리고 헤딩 인식 청킹입니다.

기여

개발 환경 설정, 코드 규칙, PR 지침은 CONTRIBUTING.md를 참조하세요.

라이선스

MIT

:remote 이미지는 독점 소프트웨어인 obsidian-headless(ob CLI)를 번들로 포함합니다. 해당 소프트웨어의 package.json은 "license": "UNLICENSED"로 선언되어 있습니다(© Dynalist Inc. / Obsidian). 이는 빌드 시점에 공개 npm에서 설치되며, 여기의 MIT 라이선스는 이를 포함하지 않습니다. 사용하려면 활성 Obsidian Sync 구독이 필요합니다. :latest(로컬) 이미지에는 독점 구성 요소가 포함되어 있지 않습니다.

보안

취약점은 비공개로 신고해 주세요 — SECURITY.md를 참조하세요.

Available Tools

33 tools
vault_create_taskCreate TaskA

Create a correctly-formatted task in one call — description, target heading, dates, priority, block_id, and optional checklist sub-items. The task is created as todo (using the status registry's todo symbol, [ ] by default) with ➕ today auto-stamped — starting work is vault_update_task's job. Metadata is written in the format the vault's Tasks plugin is configured for (emoji unless the plugin config says Dataview).

Example: vault_create_task({ path: "TASKS.md", description: "Fix login bug", block_id: "fix-login", heading: "Active", priority: "high", due: "2026-09-15" }) Example: vault_create_task({ path: "TASKS.md", description: "Sub-bug", block_id: "sub-bug", parent_block_id: "fix-login", due: "2026-09-01" }) — full sub-task under a parent identified by block_id Example: vault_create_task({ path: "TASKS.md", description: "Quick fix", block_id: "quick-fix", parent_line: 42 }) — sub-task under a parent identified by line number Example: vault_create_task({ path: "TASKS.md", description: "Mid-priority", block_id: "mid-priority", heading: "Active", position: 3 }) — insert as the 3rd card in the lane

When to use: Creating a new task card on a board or in a note. Guarantees correct field ordering (description → priority → 🔁 recurrence → 🏁 onCompletion → ➕ created → 🛫 start → ⏳ scheduled → 📅 due → 🆔 task_id → ⛔ depends_on → ^block_id) so the card round-trips through vault_list_tasks with all fields intact. For lightweight checklist items under an existing card (no metadata), use vault_update_task's add_subtasks param instead.

Parameters:

  • heading is required on Kanban boards (notes with kanban-plugin frontmatter).

  • parent_block_id / parent_line: the same pair vault_update_task uses (block_id / line). Pass at most one. Either is mutually exclusive with heading — a sub-task lives wherever its parent lives.

  • position: Kanban boards with new-card-insertion-method set to "prepend" default to "top" instead of "bottom". Ignored when no heading or when placing under a parent.

  • priority: the plugin ranks "no signifier" (normal priority) between medium and low.

  • recurrence: a rule ending "when done" bases the next occurrence on the completion day.

  • due / scheduled / start: omit a date rather than guessing — an absent 📅 means "no deadline".

Errors:

  • "note not found" — path does not exist

  • "path must end in …" — add the .md extension

  • "absolute path blocked" / "path traversal blocked" / "hidden path blocked" — use a vault-relative path with no hidden (dot-prefixed) file or folder in it

  • "heading required for Kanban boards" — kanban-plugin note without heading

  • "heading "X" not found; available: ..." — no heading matches; the error lists the note's headings

  • "cannot place at position N under "X" — the heading appears N times" — integer position on a note with duplicate heading names; rename one section to make it unique

  • "parent task not found" — parent_block_id or parent_line doesn't resolve to a task (message names the blockId or line tried), or the line is inside a fenced code block or %% %% comment

  • "checkbox "[c]" is a NON_TASK status" — the parent task's checkbox char is typed NON_TASK in the Tasks plugin's status registry, so it is not a task; to change that, retype it there and restart the server

  • "no checkbox symbol for status ..." — the status registry has no symbol for the todo status and the built-in default is retyped; update the plugin's status registry to include a todo symbol, then restart the server

  • "parentBlockId and parentLine are mutually exclusive" — both parent_block_id and parent_line were passed; drop one

  • "parent and heading are mutually exclusive" — a parent (parent_block_id or parent_line) and heading were both passed; drop one

  • "blockId ... already exists in this note" — pick a block_id not yet used in the note

  • "blockId ... contains invalid characters" — block_id must match [a-zA-Z0-9-]+

  • "description is empty" / "subtasks cannot contain an empty item" — whitespace-only description or checklist item

  • "description must be a single line" / "subtasks items must be a single line" — a task is one file line; a line break in the text would split its metadata onto a line the parser never reads

  • "taskId ... contains invalid characters" / "dependsOn entry ... contains invalid characters" — task_id and every depends_on entry must match [a-zA-Z0-9_-]+ (the Tasks plugin's id grammar)

  • "unrecognized recurrence rule ..." — the rule text is not Tasks-plugin natural language; written as-is it would silently never recur

  • "invalid date" — a date param fails calendar validation

  • "concurrent write in progress" — another write to this note is in flight; retry

Obsidian syntax: The Tasks plugin reads metadata off the END of a task line. A trailing signifier in description or subtasks text (an emoji field like "🔁 every week", or a Dataview [key:: value] field) that the plugin's parser recognizes as a field — followed only by other recognized fields — is read back as metadata, not text. Whether it is captured depends on the field's value grammar: 🔁 reads any trailing words as its recurrence rule, while 📅 followed by non-date words stays description text. The same interference can change the value an adjacent field reads back with, or make a field appear that was never set. The write still succeeds either way; when the stored line would read back differently than submitted, the result carries an advisories array naming each divergence.

Returns: JSON { path, line, description, block_id, heading, subtasks, changes, advisories } — line is the new card's 1-based position; heading is the nearest heading above the new task (omitted when the note has none); subtasks lists each checklist item written as { line, description } (omitted when none) — checklist items carry no block_id, so line is the handle for a follow-up update; changes lists every field written as "field: before → after", with "(none)" for an absent value; advisories (omitted when the line round-trips clean) lists one sentence per place the stored line parses back differently than submitted — see Obsidian syntax above.

ParametersJSON Schema
NameRequiredDescriptionDefault
dueNoDeadline (📅), YYYY-MM-DD, calendar-validated. Omit when there is no deadline.
pathYesVault-relative path to the note (must end in ".md"). The note must already exist. Use the exact letter case.
startNoEarliest day work can begin (🛫), YYYY-MM-DD, calendar-validated.
formatNoField format. Default: auto-detected from .obsidian/ config, falling back to emoji.
headingNoTarget heading. On a regular note, omit to append at end of body.
task_idNoTasks plugin 🆔 identifier other tasks can name in depends_on.
block_idYesThe ^block-id for stable identification — letters, digits, and hyphens only ([a-zA-Z0-9-]+). Must be unique within the note.
positionNoWhere within the heading section the task is placed. "top" or "bottom" for the extremes; an integer (1-based) for an exact position among the lane's top-level cards (sub-tasks move with their parent and are not counted). Position 1 is the first card. A position past the card count lands directly below the last card (unlike "bottom", which appends after any non-task text at the end of the section). Defaults to bottom.
priorityNoPriority signifier (🔺⏫🔼🔽⏬). Omit for normal priority — no signifier is written.
subtasksNoChecklist item descriptions — created as indented todo lines under the card (no metadata). For full sub-tasks with dates, priority, and block_id, make a separate call with parent_block_id.
scheduledNoDay the work is planned for (⏳), YYYY-MM-DD, calendar-validated.
depends_onNoTasks plugin ⛔ dependency IDs (🆔 values of other tasks). Non-empty; omit when there are no dependencies.
recurrenceNoTasks plugin 🔁 rule in natural language (e.g. "every week", "every month on the 15th", "every 2 weeks when done"). Completing the task spawns its next occurrence.
descriptionYesThe task text (before metadata fields).
parent_lineNo1-based line number of an existing task to nest under as a sub-task. Fragile if the file changed since the line was read.
on_completionNoTasks plugin 🏁 onCompletion action. "delete" removes the task line on completion; "keep" leaves it in place.
parent_block_idNo^block-id (without the ^) of an existing task to nest under as a sub-task.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the annotations (readOnly=false, idempotent=false, destructive=false), it discloses that the task is created in todo status with a symbol pulled from the status registry, that ➕ today is auto-stamped, that metadata is written in the plugin-configured emoji/Dataview format, and that writes can fail with 'concurrent write in progress'. It also explains the advisory mechanism when a stored line parses back differently — behavior an agent could not infer from annotations alone.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded correctly: purpose, examples, when-to-use, then parameters, errors, syntax caveats, returns. Every section is scannable, but the error catalog and the Obsidian-syntax paragraph make the text very long relative to the core instruction, and some error strings duplicate constraints already implied by the schema (block_id charset, single-line description).

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, yet the description fully documents the return object (path, line, description, block_id, heading, subtasks, changes, advisories) and explains when fields are omitted. For a 17-parameter mutation tool with no output schema, the description supplies everything needed to call it and interpret the response.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds cross-parameter and vault-specific semantics the schema lacks: heading is required on kanban-plugin notes, parent_block_id/parent_line are mutually exclusive with each other and with heading, priority's 'no signifier' ranks between medium and low, and 'when done' recurrence bases the next occurrence on the completion day. These are genuine additions, though several details (position behavior, date omission) largely restate the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a specific verb+resource ('Create a correctly-formatted task in one call') and enumerates the fields written. It explicitly distinguishes itself from siblings: new tasks are its job, while 'starting work is vault_update_task's job' and lightweight checklist items belong to vault_update_task's add_subtasks. No schema-opening is required to route between the two tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Has a dedicated 'When to use' line plus four concrete call examples, and names the alternative use-path for lightweight checklist items. It also states preconditions and exclusions: heading required on Kanban boards, parent_block_id/parent_line mutually exclusive with heading, 'omit a date rather than guessing'. This is explicit when/when-not/alternative guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

vault_delete_memoryDelete Memory EntryA
Destructive

Delete a single dated entry from a memory file in About Me/. Both date and entry text are required for exact matching — ensures only the intended entry is removed.

Example: vault_delete_memory({ file: "Opinions", section: "AI tooling & memory (newest first)", date: "2026-05-01", entry: "Prefer X over Y" })

When to use: Removing an entry that was wrong when it was written — a mistake, a misattribution, or something never true. Memory files are append-only by default, so do NOT delete to reflect a change: append the new state via vault_update_memory instead (newest-first naturally supersedes). The exception is a file whose frontmatter declares entry-policy: living (check via vault_list_memory_files) — a current-state file where deleting an expired entry is the intended maintenance. Call vault_get_memory(file, section) first to see exact entry text for matching. Prefer vault_delete_note for deleting entire non-protected notes.

Parameters:

  • date + entry together uniquely identify the bullet line within the given section. If multiple entries share the same date and text, deletion fails as ambiguous.

  • section scopes the match — an identical entry under a different heading is not found. Section matching is case-insensitive, with or without the "(newest first)" suffix.

Errors:

  • "memory file not found" — file does not exist in About Me/; call vault_list_memory_files to discover valid names.

  • "memory file must not start with a dot" / "memory file must be a bare name without path separators" — pass the file's bare name: no folder or slash, and no leading dot (that would target a hidden file; memory files are always visible notes).

  • "date must be a real ISO calendar date" — date only accepts an existing calendar date in bare YYYY-MM-DD form. A hand-edited bullet carrying an impossible date cannot be targeted by this tool — remove it with vault_delete_span or a manual edit.

  • "section not found: …" — no H2 heading matches; the error lists the file's available sections

  • "no entry matching …" — no bullet matched the given date and entry text; verify exact text via vault_get_memory(file, section).

  • "ambiguous: N entries match …" — more than one identical bullet exists in the section (e.g. from hand edits, sync conflicts, or entries predating duplicate protection; vault_update_memory refuses to write exact duplicates). Remove the extra copy with vault_delete_span (pass first_match: true — identical lines make every anchor ambiguous) or a manual edit, then retry.

  • "refusing memory write: … would shrink content" — safety guard blocked a write that would remove more than half the file. Re-read with vault_get_memory to confirm current content; an entry that really is that large needs vault_delete_span or a manual edit.

Returns: Confirmation message.

ParametersJSON Schema
NameRequiredDescriptionDefault
dateYesISO YYYY-MM-DD date of the entry (e.g. "2026-05-01"). Must match the date shown by vault_get_memory.
fileYesMemory file name without .md (e.g. "Principles"). Use the exact letter case.
entryYesExact entry text as shown by vault_get_memory — without the "- **YYYY-MM-DD**: " prefix or bullet.
sectionYesH2 section heading containing the entry. Matched case-insensitively, with or without the "(newest first)" suffix.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare destructiveHint=true and readOnlyHint=false, and the description is consistent with that while adding far more: the append-only default policy, the `entry-policy: living` exception, the ambiguity failure mode, and the 'refusing memory write: would shrink content' safety guard. This is unusually rich disclosure of destructive-operation semantics.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose, example, when-to-use, parameters, and errors are cleanly headed and front-loaded, and the length is justified by a genuinely complex matching/error surface. The error list is exhaustive to the point of mild verbosity, but each entry maps to a distinct actionable recovery path.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a destructive, non-idempotent tool with four required parameters and no output schema, the definition covers prerequisites, matching semantics, all failure modes with recovery steps, and the return value. Nothing needed to invoke it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds real meaning beyond it: date+entry together uniquely identify a bullet line, duplicate date+text pairs fail as ambiguous, and section scoping means an identical entry under a different heading will not match. Case-insensitive section matching is already covered by the schema, so it is not fully additive.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource ('Delete a single dated entry from a memory file in About Me/') and immediately scopes it against siblings by clarifying it removes one entry, not a note. The example makes the granularity concrete, so an agent can distinguish this from vault_delete_note without reading either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives explicit when-to-use ('removing an entry that was wrong when it was written'), explicit when-NOT-to-use ('do NOT delete to reflect a change: append via vault_update_memory instead'), and a named exception (files with `entry-policy: living`). It also routes to alternatives for adjacent cases (vault_delete_note for whole notes, vault_delete_span for duplicate/impossible-date lines).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

vault_delete_noteDelete NoteA
Destructive

Delete a markdown note, moving it to the vault's .trash/ folder or removing it for good as the vault's Obsidian "Deleted files" setting directs.

Example: vault_delete_note({ path: "Scratch/temp.md" }) Example: vault_delete_note({ path: "Archive/2024/old.md", prune_empty_folders: true }) — also remove "Archive/2024" (and "Archive") if deleting the note empties them.

When to use: Removing a note you no longer need. Prefer vault_delete_memory for removing individual dated entries from About Me/ memory files. To relocate a note, use vault_move_note instead. To replace a note's content, use vault_write_note with overwrite: true instead.

Behavior:

  • Unless the server syncs through Obsidian Sync, the "Deleted files" setting (trashOption in .obsidian/app.json) decides the outcome:

    • "Move to system trash" (system, also what an absent setting means) moves the note to .trash/, since the server has no system trash. The server deletes its own copies there after its TRASH_RETENTION_DAYS setting (default 30 days, or never when set to none), never touching notes Obsidian trashed.

    • "Move to Obsidian trash" (local) moves the note to .trash/ and keeps it forever.

    • "Permanently delete" (none) removes the note for good.

  • When the server syncs through Obsidian Sync, the setting is bypassed and the note is always deleted for good; recover it from Sync's version history (1 month on Standard, 12 months on Plus).

  • The caller can't choose or see the outcome in advance; the returned message says which happened.

  • Links to the note from other notes become broken (detectable via vault_get_backlinks). Protected paths (About Me/ and the daily notes folder (read from DAILY_NOTES_FOLDER or .obsidian/daily-notes.json, defaulting to Daily Notes/)) are refused.

Parameters:

  • prune_empty_folders removes each parent folder the delete leaves with zero entries, up to but never including the vault root; a folder holding any file, even a hidden .DS_Store, is kept. Pruning runs after the delete or trash move and is best-effort: a folder that can't be removed never fails the call. Without it, empty folders stay, matching Obsidian.

Errors:

  • "cannot delete protected path" — the path sits under a protected folder; use vault_delete_memory for memory entries

  • "path must end in …" — add the .md extension

  • "absolute path blocked" / "path traversal blocked" / "hidden path blocked" — use a vault-relative path with no hidden (dot-prefixed) file or folder in it

  • "concurrent write in progress" — another write to this note is in flight; retry

  • "note not found: …" — the note does not exist; verify the path with vault_list_notes before deleting

  • "cannot move to trash … — 100 collisions in .trash/" — .trash/ already holds this name and its numbered copies ("Plan 1.md" … "Plan 100.md"); clear old trash copies, then retry

  • any other "cannot move to trash …" — the .trash/ move failed (e.g. a plain file blocks a needed folder); the note stays put; fix .trash/, then retry

  • any other "cannot delete …" — the permanent delete failed (e.g. permissions); the note stays put; fix the cause, then retry

  • "cannot read trash config from .obsidian/app.json" — the file exists but is unreadable; the delete is blocked because a guessed setting could let the retention sweep remove a note set to be kept forever; repair the file, then retry

  • "cannot read daily notes config from .obsidian/daily-notes.json" — the file exists but is unreadable, so the daily notes folder to protect is unknown; repair it, or set DAILY_NOTES_FOLDER or PROTECTED_PATHS, then retry

Returns: Confirmation message naming the outcome — "Deleted " for permanent removal, "Moved to trash ()" when the note landed in .trash/. Notes how many empty folders were pruned when any were.

ParametersJSON Schema
NameRequiredDescriptionDefault
pathYesVault-relative path of the note to delete, including the ".md" extension. Use the exact letter case.
prune_empty_foldersNoWhen true, also remove parent folders the delete leaves empty. Default false.

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well beyond the destructiveHint annotation: it discloses that the outcome depends on the vault's trashOption setting, how Obsidian Sync bypasses that setting (with recovery windows), that the caller cannot pre-select or see the outcome, that backlinks break, and that protected paths are refused. This is unusually rich behavioral disclosure for a mutation tool. Minor deduction only because the very long error catalog is more error-reference than behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads purpose, then usage, behavior, parameters, errors, returns — a sensible order. It is long, but most of the length is non-redundant behavioral/error detail. The error catalog is the one section that could be trimmed, keeping it just short of a 5.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given a two-parameter destructive tool with no output schema, the description covers outcome variability, protected paths, retention, failure modes, and the returned confirmation strings. Nothing an agent needs to call this correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3. The description nonetheless adds real meaning for prune_empty_folders: it prunes each emptied parent up to but not including the vault root, is best-effort and never fails the call, and keeps folders containing even hidden files like .DS_Store.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Delete a markdown note') with the scope of what happens to the file (trash vs permanent). It names the sibling operations it is not (vault_delete_memory, vault_move_note, vault_write_note), so an agent can route correctly without opening sibling schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Has an explicit 'When to use' line plus three named alternatives with the exact condition that selects each (use vault_delete_memory for dated memory entries, vault_move_note to relocate, vault_write_note overwrite to replace). Nothing is left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

vault_delete_spanDelete SpanA
Destructive

Delete a contiguous block of whole lines from a note's body by referencing short anchor substrings instead of reproducing the full block text. Each anchor locates a full line — the entire line is selected, not just the matching substring. Case-sensitive matching. Properties are preserved; YAML formatting may be normalized to block style on first edit. Operates on the body only.

Example: vault_delete_span({ path: "Tracker.md", start_anchor: "| 2024-03-02 | Acme" }) — deletes the one table row whose line contains that fragment. Example: vault_delete_span({ path: "Notes/Plan.md", start_anchor: "> [!warning] Stale", end_anchor: "remove after launch" }) — deletes from the start anchor line through the end anchor line.

When to use: Removing a block you have already read — a table row, callout, or run of list items — where reproducing it exactly as old_text would be error-prone. Pick a short, unique fragment of the first line for start_anchor and, for a multi-line block, the last line for end_anchor. Prefer vault_replace_in_note for small in-place edits (this tool only deletes). To replace a block, use vault_replace_span (one atomic step).

Parameters:

  • start_anchor + end_anchor define a line range, not a text range (never cuts mid-line). Omit end_anchor for a single-line delete. The empty lines above and below the removed lines join into one gap that keeps the larger of the two counts (only at the end of the note, the count above drops by one); no other empty line in the note changes. A line holding only spaces or tabs counts as text, not as an empty line.

  • end_anchor is searched at or after the start line, so the span can never run backward; it must be unique among those lines. If both match the same line, only that one line is deleted.

  • first_match applies to both anchors independently — when an anchor matches multiple lines, takes the first instead of erroring.

Errors:

  • "note not found" — verify path with vault_list_notes

  • "path must end in …" — add the .md extension

  • "start anchor not found" / "end anchor not found" — no line contains the fragment (for end_anchor, none at or after the start line); verify with vault_read_note

  • "ambiguous start anchor …" / "ambiguous end anchor …" — the anchor matches multiple lines; use a longer fragment or set first_match: true

  • "absolute path blocked" / "path traversal blocked" / "hidden path blocked" — use a vault-relative path with no hidden (dot-prefixed) file or folder in it

  • "concurrent write in progress" — another write to this note is in flight; re-read the note and retry

Returns: Confirmation with the number of lines the span covered and a preview of them, cut at 80 characters.

ParametersJSON Schema
NameRequiredDescriptionDefault
pathYesVault-relative path to the note, including the ".md" extension (e.g. "Tracker.md", "Notes/Plan.md"). Use the exact letter case.
end_anchorNoShort substring that identifies the LAST line of the block. The entire line is selected. Omit to delete just the single line containing start_anchor.
first_matchNoIf an anchor matches more than one line, delete using the first match instead of erroring (default: false — ambiguity is an error).
start_anchorYesShort, unique substring that identifies the first line of the block (case-sensitive). The entire line is selected, not just the substring. Pick a brief fragment — do not paste the whole block.

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare destructiveHint=true and idempotentHint=false, but the description goes well past them: case-sensitive matching, properties preserved with YAML normalized to block style, body-only scope, the empty-line merge rule, forward-only end_anchor search, first_match semantics, and a full error catalogue including 'concurrent write in progress' and path-safety blocks. That is unusually rich disclosure for a destructive operation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core mechanism, then examples, then routing guidance, then parameter and error detail — a logical order. It is long, and the parenthetical about empty-line counts at the end of the note is arguably over-specified for a description, but almost every sentence carries actionable information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, and the description compensates by stating the return value (line count plus an 80-character preview) and enumerating the failure modes with a remediation for each. For a destructive, 4-parameter tool, an agent has everything needed to call it correctly and recover from errors.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is already 100%, yet the description adds semantics the schema does not: anchors define a line range rather than a text range and never cut mid-line, end_anchor is searched at or after the start line so the span cannot run backward, and first_match applies to each anchor independently. It also clarifies that a whitespace-only line counts as text, not as empty.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('delete a contiguous block of whole lines from a note's body') and immediately distinguishes its mechanism (anchor substrings instead of full block text) from the siblings that do similar work. An agent can tell it apart from vault_replace_in_note and vault_replace_span without opening a schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

An explicit 'When to use' section names the scenario (a block already read, where reproducing old_text is error-prone) and names the alternatives: vault_replace_in_note for small in-place edits, vault_replace_span for block replacement. Both the positive case and the boundary are stated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

vault_find_orphansFind OrphansA
Read-onlyIdempotent

Find notes with no incoming links from other notes or canvases — orphans are disconnected from the knowledge graph and may be forgotten or need linking. A note that only links to itself still counts as an orphan (self-links are ignored).

Example: vault_find_orphans({}) Example: vault_find_orphans({ exclude_folders: ["Archive"], limit: 10 })

When to use: Vault maintenance — surfacing notes to integrate into the graph. Link an orphan by mentioning it from a relevant note with vault_patch_note. Prefer vault_get_backlinks to check the connectivity of one specific note rather than scanning the whole vault.

Parameters:

  • The daily notes folder is resolved on each call (DAILY_NOTES_FOLDER → .obsidian/daily-notes.json → "Daily Notes"). ORPHAN_EXCLUDE_FOLDERS replaces the defaults. For unreadable daily settings, the server logs a warning and uses "Daily Notes".

  • exclude_folders replaces the defaults (including an environment override), it does not add to them — include the defaults yourself to keep them. Pass [] for no exclusions. Each entry names a whole folder, subfolders included ("Projects" also excludes "Projects/Archive" but not "ProjectsOld/"), ignoring ASCII letter case.

  • limit applies after exclusions and sorting by most recently modified. Nothing in the response signals truncation: exactly limit results may mean more exist, so raise limit to check.

Errors:

  • An empty array means no orphans were found (after exclusions), not an error.

  • "too many excluded folders" — pass a shorter exclude_folders list, then retry.

Returns: JSON array of note metadata (path, title, tags, related, folder, type, created, modified, bytes, leading_callout?, additional_properties?), sorted by most recently modified. bytes is the on-disk file size.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax results (default 50)
exclude_foldersNoFolder paths to exclude (e.g. Projects; default: daily notes folder, Templates, "About Me")

TDQS

A4.5/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint, destructiveHint=false and openWorldHint=false, so the safety profile is fully covered. The description adds genuinely useful behavior beyond that: the daily-notes folder resolution order, that ORPHAN_EXCLUDE_FOLDERS replaces defaults, and — most importantly — that truncation is invisible because exactly 'limit' results may mean more exist.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the definition and edge case, then cleanly sectioned into When to use / Parameters / Errors / Returns. Nearly every sentence earns its place, though the two inline Example calls are somewhat redundant given the parameter bullets, and the daily-notes resolution detail is dense for a two-parameter tool.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, and the description compensates fully with a Returns section enumerating the metadata fields (path, title, tags, folder, type, created, modified, bytes, etc.) and the sort order. It also documents the empty-array and 'too many excluded folders' cases, so nothing needed to call or interpret it is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description goes well past the schema: exclude_folders replaces rather than augments defaults (include defaults yourself, pass [] for none), each entry covers subfolders, matching ignores ASCII case, and limit applies after exclusions and most-recently-modified sorting. This is meaning the schema does not carry.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description gives a specific verb and resource ('Find notes with no incoming links') and immediately defines the term 'orphans' as notes disconnected from the knowledge graph. It even resolves the edge case of self-links, so an agent knows exactly what this tool returns and how it differs from link-listing siblings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It states the use case ('Vault maintenance — surfacing notes to integrate into the graph'), names the follow-up action (link via vault_patch_note), and explicitly routes the agent elsewhere for the adjacent task ('Prefer vault_get_backlinks to check the connectivity of one specific note'). Both when-to-use and the alternative are given.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

vault_get_daily_noteGet Daily NoteA
Read-onlyIdempotent

Read a daily note by date, using the vault's configured Daily Notes folder and filename date format. Each setting comes from the DAILY_NOTES_FOLDER / DAILY_NOTES_FORMAT env vars, falling back to the vault's .obsidian/daily-notes.json, then to "Daily Notes" and YYYY-MM-DD.

Example: vault_get_daily_note({ date: "2026-05-13" }) Example: vault_get_daily_note({}) — returns today's daily note

When to use: When you need today's or a specific date's daily note. Handles path resolution automatically using the vault's Obsidian config — you don't need to know the folder name or filename format. To append content to a daily note section, use the returned path with vault_patch_note. Use vault_recent_notes to review recent vault activity around a date (not date-filtered — returns globally recent notes).

Parameters:

  • date: past and future dates are both valid — the tool resolves the configured path for any date and reports exists: false if the note hasn't been created yet.

Errors:

  • "invalid date" — use YYYY-MM-DD format

  • "daily note format contains unsupported token(s): ..." — the configured format uses tokens the server cannot reproduce (ordinals like Do/Mo, dd, d, e, k/kk, w, Q, Z/ZZ, or the L-family localized formats); change the format in Obsidian or set DAILY_NOTES_FORMAT to a supported alternative

Returns: JSON with path (string — resolved vault-relative path), content (string|null — full note body, or null when the note doesn't exist), and exists (boolean). When exists is false, create the note with vault_write_note using the returned path.

ParametersJSON Schema
NameRequiredDescriptionDefault
dateNoYYYY-MM-DD (e.g. "2026-05-13", "2025-12-31"). Defaults to today in the server's timezone. Invalid formats like "May 13" return an error.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly/idempotent/non-destructive, but the description goes well beyond them: it discloses the config resolution chain (env vars → .obsidian/daily-notes.json → defaults), the exists:false contract for not-yet-created notes, and two named error conditions with remediation. This is rich behavioral context the annotations cannot supply.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with purpose, then examples, when-to-use, parameters, errors, and returns — a clean, scannable structure where nearly every line carries information. It runs slightly long (the full env-var fallback chain and the exhaustive unsupported-token list) but that detail is load-bearing for error diagnosis, not filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description fully specifies the return shape (path, content, exists) and even the follow-up action when exists is false. Config resolution, valid inputs, error conditions, and sibling handoffs are all covered, so nothing an agent needs to call this correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% for the single date parameter, so the schema already carries format and default semantics (baseline 3). The description adds meaning beyond it: past and future dates are both valid, and a non-existent date yields exists:false rather than an error. That is genuine added semantics, though it stops short of anything like timezone nuance beyond the schema's server-timezone note.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (Read) and resource (daily note by date) and immediately scopes it with the vault's configured folder/format resolution. It explicitly distinguishes itself from siblings: vault_recent_notes is described as 'not date-filtered', and vault_patch_note is named for appending. An agent can route correctly without opening any other schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

A dedicated 'When to use' section covers both today's and arbitrary dates, and names two alternatives with the conditions that select them (vault_patch_note to append content, vault_recent_notes for date-adjacent vault activity). Exclusions and handoffs are explicit rather than inferred.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

vault_get_memoryGet MemoryA
Read-onlyIdempotent

Read semantic memory from About Me/ files. These are structured memory files containing dated bullet entries organized under H2 headings. With file: single file content. With file+section: just that H2 section's entries. No args: all files concatenated (frontmatter stripped) — can be large. Returns empty string when no memory files exist yet.

With file+on_or_after: returns structured JSON entries dated on or after the boundary date (inclusive), in document order (newest first when new entries go at the top, the default). Add section to scope to one H2 section; omit it to read every H2 section's entries in the file, each keeping its section attribution. It gives complete coverage from a known date without re-parsing the section.

Example: vault_get_memory({ file: "Principles", section: "Decision heuristics (newest first)" }) Example: vault_get_memory({ file: "Opinions", section: "Code patterns", on_or_after: "2026-09-01" }) Example: vault_get_memory({ file: "Opinions", on_or_after: "2026-09-01" }) — every section's entries since the date

When to use: Reading user preferences, principles, opinions, or other persistent context stored in About Me/ files. Call vault_list_memory_files first to discover valid file and section names. Use on_or_after when you need entries from a known date forward (e.g. reconciliation boundaries). Prefer vault_read_note for reading non-memory notes.

Errors:

  • "section requires a file" / "on_or_after requires a file" — section or on_or_after was passed without file; add file

  • "memory file not found" — file does not exist in About Me/; call vault_list_memory_files to discover valid names

  • "memory file must not start with a dot" / "memory file must be a bare name without path separators" — pass the file's bare name: no folder or slash, and no leading dot (that would be a hidden file; memory files are always visible notes)

  • "section not found: …" — no H2 heading matches; the error lists the file's available sections

  • "date must be a real ISO calendar date" — on_or_after must be a valid YYYY-MM-DD date

Returns: Without on_or_after, raw markdown text. With on_or_after, JSON { entries, total, on_or_after } where each entry is { file, section, date, text } — text is the full raw entry markdown (bullet + continuation lines, wikilinks intact), same shape as vault_memory_recall entries. An empty match returns { entries: [], total: 0 }.

ParametersJSON Schema
NameRequiredDescriptionDefault
fileNoMemory file name without .md (e.g. "Principles", "Opinions"). Use the exact letter case.
sectionNoH2 section heading (e.g. "Decision heuristics (newest first)"). Matched case-insensitively, with or without the "(newest first)" suffix. Omit to read the whole file (with on_or_after, every H2 section's entries). Call vault_list_memory_files first to discover valid names.
on_or_afterNoInclusive date filter (YYYY-MM-DD). When provided with file, returns structured JSON entries dated on or after this date instead of raw markdown. Requires a file; add section to scope to one H2 section.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly/idempotent/non-destructive, and the description adds substantial context beyond them: the empty-string return when no memory files exist, the 'can be large' warning on the no-arg concatenation, frontmatter stripping, the markdown-vs-JSON output switch keyed on on_or_after, and a full enumerated error catalogue with remedies. This is genuinely rich disclosure.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well front-loaded: the first sentence gives the core purpose, modes follow, then examples, then usage, errors, and returns. Nothing is wasted for a three-mode tool, but three near-duplicate examples plus a six-item error list make it longer than strictly necessary.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, so the description correctly carries the return contract itself, specifying raw markdown vs JSON { entries, total, on_or_after } and the entry shape, plus the empty-match case. Combined with the error catalogue and prerequisites, an agent has everything needed to call and interpret this tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the per-parameter definitions are already documented. The description still adds meaning the schema does not: inter-parameter dependencies ('section requires a file', 'on_or_after requires a file'), how section scopes the result set, and how on_or_after changes the output type. It stops short of the full 5 because format details (ISO date shape, case-insensitivity) largely mirror the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Read semantic memory from About Me/ files') and immediately distinguishes the three behavioral modes (file only, file+section, no args). It also names the siblings it is not (vault_read_note for non-memory notes, vault_list_memory_files for discovery), so an agent can route correctly without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Has an explicit 'When to use' block (user preferences, principles, opinions, persistent context), an explicit prerequisite (call vault_list_memory_files first), a conditional trigger for on_or_after (reconciliation boundaries), and an explicit alternative rule ('Prefer vault_read_note for reading non-memory notes'). When/when-not/alternatives are all present.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

vault_insert_at_anchorInsert at AnchorA

Insert content as whole lines before or after a specific line identified by a short anchor substring. Case-sensitive matching; an anchor matching more than one line is an error unless first_match is set. Same anchor resolution as vault_delete_span. Properties are preserved; YAML formatting may be normalized to block style on first edit. Operates on the body only.

Example: vault_insert_at_anchor({ path: "Tracker.md", anchor: "| 2024-03-02 | Acme", position: "after", content: "| 2024-03-03 | Beta Corp | New entry |" }) — inserts a new table row after the matched row. Example: vault_insert_at_anchor({ path: "Notes/Plan.md", anchor: "## Phase 2", position: "before", content: "> [!note] Phase 1 must close before this starts.\n" }) — inserts a callout and a blank line above the Phase 2 heading.

When to use: Adding content at a precise location identified by a nearby line's text, without needing to know the heading structure. Good for inserting rows into tables, adding items into lists at a specific position, or placing content relative to a known landmark line. Prefer vault_patch_note for heading-targeted inserts (append/prepend to a section). Prefer vault_replace_span when replacing a block rather than inserting next to it.

Parameters:

  • anchor locates a full line — the insert never splits a line.

  • content is inserted verbatim — blank lines inside it are kept, and a trailing newline adds a blank line after the inserted block.

Errors:

  • "note not found" — verify path with vault_list_notes

  • "path must end in …" — add the .md extension

  • "anchor not found" — fragment not on any line; verify with vault_read_note

  • "ambiguous anchor …" — the anchor matches multiple lines; use a longer fragment or set first_match: true

  • "absolute path blocked" / "path traversal blocked" / "hidden path blocked" — use a vault-relative path with no hidden (dot-prefixed) file or folder in it

  • "concurrent write in progress" — another write to this note is in flight; re-read the note and retry

  • "content contains a control character" — content includes a non-printable control byte; remove it before writing

Obsidian syntax: content is Obsidian Flavored Markdown (no escaping applied). Watch for: #word = tag, [[ = wikilink, %% = comment block.

Returns: Confirmation message "Inserted lines <before|after> anchor in " — N counts the lines content supplied.

ParametersJSON Schema
NameRequiredDescriptionDefault
pathYesVault-relative path to the note, including the ".md" extension (e.g. "Notes/Plan.md", "Tracker.md"). Use the exact letter case.
anchorYesShort, unique substring on the line to insert next to (case-sensitive). Pick a brief fragment — do not paste the whole line.
contentYesContent to insert (one or more lines).
positionYes"before" places the content on the lines above the anchor line; "after" places it on the lines below. The anchor line itself is never changed.
first_matchNoIf the anchor matches more than one line, use the first match instead of erroring (default: false — ambiguity is an error).

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the annotations (readOnlyHint=false, destructiveHint=false, idempotentHint=false), the description discloses case-sensitive matching, the ambiguity-is-an-error rule unless first_match is set, that properties are preserved but YAML may normalize to block style on first edit, and that only the body is touched. It also enumerates nine specific error strings with remedies, which annotations cannot convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The content is long but well-organized and front-loaded: core behavior, two worked examples, when-to-use routing, parameter notes, then errors. The error catalogue is extensive but each entry earns its place by pairing a message with a fix. Slightly verbose overall, but no sentence is pure filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, but the description supplies the return contract verbatim ('Inserted <N> lines <before|after> anchor in <path>') and defines N. Combined with the error catalogue and Obsidian syntax caveats (#, [[, %%), an agent has everything needed to call this correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds real meaning: the anchor locates a full line and the insert never splits one, content is inserted verbatim with internal blank lines preserved, and a trailing newline adds a blank line after the block. These are semantics the schema does not capture.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource ('Insert content as whole lines before or after a specific line identified by a short anchor substring') and immediately scopes it ('Operates on the body only'). It explicitly distinguishes itself from vault_patch_note and vault_replace_span, so an agent can route correctly without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It provides an explicit 'When to use' section with concrete scenarios (table rows, list items, landmarks) and names two alternatives with the conditions that select them: vault_patch_note for heading-targeted inserts and vault_replace_span when replacing a block rather than inserting next to it. Nothing is left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

vault_list_filesList FilesA
Read-onlyIdempotent

List non-markdown files in the vault or a folder — images, canvases, PDFs, data files — with per-file byte sizes and per-extension counts.

Example: vault_list_files({}) — every non-markdown file in the vault Example: vault_list_files({ folder: "attachments" }) Example: vault_list_files({ extensions: [".png", ".jpg"], limit: 20 })

When to use: discovering what files exist before reading them with vault_read_file. vault_list_notes and vault_search_by_folder cover only markdown notes, and beyond notes vault_search indexes only canvas, PDF, and text-format files, so this is the discovery surface for everything else. For the files one specific note links to, prefer vault_get_outgoing_links.

Behavior: Reads the filesystem rather than the search index, so use the folder's exact letter case; on a case-sensitive filesystem a different case finds nothing.

Errors:

  • A visible folder containing no files — or one that doesn't exist — returns an empty listing, not an error.

  • "absolute path blocked" / "path traversal blocked" / "hidden path blocked" — the folder starts at the filesystem root, escapes the vault (e.g. "../elsewhere") or names the vault root itself (e.g. "."), or is hidden like ".obsidian" (hidden folders are not listable, matching Obsidian); use a vault-relative folder outside hidden folders, and omit folder to list the whole vault.

Returns: JSON with files (array of { path, extension, bytes }, sorted by path; extension is "(none)" for a file without one), extension_counts (per-extension totals over the full filtered set), total (full filtered count), and truncated (true when total exceeds limit). bytes is the on-disk file size, not the delivery cost: reading an image via vault_read_file returns a copy shrunk to fit when needed, so a large listed image is still cheap to read. Text formats return verbatim, so their listed size is what a read delivers; one over 100 KiB must be read in windows with vault_read_file's start_line and limit. Only supported types are readable via vault_read_file.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax entries returned (default 50).
folderNoFolder path to search recursively (e.g. "attachments" or "Projects/media"). Omit to list the whole vault.
extensionsNoOnly include these extensions, case-insensitive, leading dot optional (e.g. [".png", "jpg"]).

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly/idempotent/non-destructive, yet the description adds substantially more: reads the filesystem rather than the index (explaining the case-sensitivity rule), empty-vs-error behavior for missing folders, three named error strings with their causes and remedies, and how 'bytes' relates to actual read cost (images shrunk, text over 100 KiB needing windowed reads).

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core purpose and three illustrations, then organized under 'When to use', 'Behavior', 'Errors', and 'Returns'. It is long for a listing tool and the Returns paragraph drifts into read-path advice, but nearly every sentence carries non-obvious information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, so the description fully documents the return shape (files/extension_counts/total/truncated, path sorting, '(none)' extension). Combined with error handling, case sensitivity, and sibling routing, an agent has everything needed to call this correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so per-parameter docs already exist and the baseline is 3. The description adds real meaning on top: it demonstrates each parameter with worked examples and supplies the case-sensitivity rule for 'folder' and the truncation semantics tied to 'limit' via the 'truncated' return field.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('List non-markdown files in the vault or a folder') with an explicit scope qualifier ('non-markdown') that immediately separates it from note-oriented siblings. The examples make the resource and filter axes concrete.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

A dedicated 'When to use' block names the alternative tools (vault_read_file, vault_list_notes, vault_search_by_folder, vault_search, vault_get_outgoing_links) and the exact condition that selects each. It also states which file types other search surfaces index, so the boundary is not left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

vault_list_memory_filesList Memory FilesA
Read-onlyIdempotent

Discovery tool — lists About Me/ memory files with each file's heading outline, per-section entry counts, and leading callout. An entry is a dated bullet line ("- YYYY-MM-DD: text"). Does NOT return actual entries.

Example: vault_list_memory_files() returns [{ file: "Principles", title: "Principles", bytes: 2048, entry_policy: "append-only", leading_callout: null, headings: [{ level: 2, text: "Decision heuristics (newest first)", entry_count: 12 }] }, ...]

When to use: Discovering what memory files and sections exist — and what each file is for — BEFORE calling vault_get_memory, vault_update_memory, or vault_delete_memory. Always call this first to get valid file and section names, and to check a file's entry_policy before pruning entries.

Behavior: Lists every .md file directly inside About Me/, sorted by file name, whether or not it has frontmatter or a scope callout. Subfolders are not read, and hidden (dot-prefixed) files are skipped.

Errors:

  • An empty or nonexistent memory folder returns an empty array, not an error.

Returns: JSON array of file outlines, each { file, title, bytes, entry_policy, leading_callout, headings } — file is the name the other memory tools take as file (no .md); bytes is the on-disk file size; headings lists H1 and H2 headings in order, with entry_count on H2s; leading_callout is the top-of-file callout ({ type, title, body }; by convention a "Scope of this file" block describing what belongs in the file), or null; entry_policy is "append-only" (the default: by convention, entries are never edited or deleted) or "living" (a current-state file whose expired entries may be pruned; declared via entry-policy frontmatter). The server does not enforce either policy.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover the safety profile (read-only, idempotent), and the description adds substantial context beyond them: hidden files are skipped, subfolders are not read, an empty folder returns an empty array instead of an error, entries are dated bullets, and entry_policy semantics are explained including that the server does not enforce either policy.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Content is front-loaded (purpose, example, when-to-use, behavior, errors, returns) and every section carries information, but the embedded JSON example is long and the description as a whole is heavier than strictly necessary for a no-argument listing tool.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description fully compensates by enumerating every returned field (file, title, bytes, entry_policy, leading_callout, headings) and its meaning, plus the error case and the entry-policy convention. An agent has everything needed to call and interpret it.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, which is the baseline-4 case. The description does enrich the semantics of the returned object fields, but those are output semantics rather than input parameter meaning, so it does not exceed the zero-parameter baseline.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource ('lists About Me/ memory files') plus exactly what is and is not included ('heading outline, per-section entry counts, and leading callout... Does NOT return actual entries'). This cleanly separates it from vault_get_memory, vault_list_files, and vault_search, which the agent can distinguish without opening schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit 'When to use' section names the downstream tools (vault_get_memory, vault_update_memory, vault_delete_memory) and the sequencing rule ('Always call this first to get valid file and section names'). It also gives a distinct reason to call it, checking entry_policy before pruning.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

vault_list_notesList NotesA
Read-onlyIdempotent

List .md file paths in the vault, optionally filtered by folder and/or glob pattern. Returns paths only — not content or metadata.

Example: vault_list_notes({ folder: "Projects" }) Example: vault_list_notes({ glob: "**/session-log.md" }) Example: vault_list_notes({ folder: "Projects", glob: "*.md" }) — the folder's top-level notes only

When to use: Browsing what exists in a folder by filename, or finding notes matching a path pattern. Prefer vault_search_by_folder when you need metadata (tags, type, related) along with paths. Prefer vault_search for content-based discovery. Use vault_read_note to read a note from the results.

Parameters:

  • folder names a whole folder and includes its subfolders: "Projects" covers "Projects/Archive" but not "ProjectsOld/". This tool reads the filesystem rather than the search index, so use the folder's exact letter case, as other results show it; on a case-sensitive filesystem a different case finds nothing.

  • glob matches each note's path inside folder (its vault-relative path when folder is omitted), case-sensitively. * stays within one folder level and ** spans any depth: with folder "Projects", ".md" lists the folder's top-level notes and "**/.md" every note under it. Returned paths are always vault-relative.

Behavior: Paths come back sorted by vault-relative path, uppercase before lowercase. Hidden (dot-prefixed) notes and folders are never listed, matching Obsidian; symlinked notes are included.

Errors:

  • A nonexistent folder or no glob matches returns an empty array, not an error.

  • "absolute path blocked" / "path traversal blocked" / "hidden path blocked" — the folder starts at the filesystem root, escapes the vault or names the vault root itself (e.g. "."), or is hidden like ".obsidian"; use a vault-relative folder outside hidden folders, and omit folder to list the whole vault.

Returns: JSON array of vault-relative path strings (e.g. ["Notes/idea.md", "Projects/plan.md"]).

ParametersJSON Schema
NameRequiredDescriptionDefault
globNoGlob pattern for note paths (e.g. "**/*session-log*.md").
folderNoVault-relative folder to list (e.g. "About Me", "Projects").

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well past the annotations (readOnly/idempotent/non-destructive), disclosing sort order (uppercase before lowercase), hidden dot-file exclusion matching Obsidian, symlink inclusion, filesystem-vs-index sourcing, case sensitivity, and exact error semantics (empty array vs 'absolute path blocked'/'path traversal blocked'/'hidden path blocked'). This is rich context an agent cannot derive from any structured field.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Long but tightly organized with headers (When to use / Parameters / Behavior / Errors / Returns), examples front-loaded after the first sentence, and no filler. Each section earns its space by covering a distinct decision an agent has to make.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 2-param, no-output-schema read tool, the definition covers purpose, routing, parameter edge cases, ordering, hidden/symlink handling, and the full error taxonomy including remediation. Nothing needed to call it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Even though schema coverage is 100%, the description adds real meaning: folder includes subfolders but 'Projects' does not match 'ProjectsOld/', exact letter case is required on case-sensitive filesystems, and glob semantics are spelled out ('* stays within one level, ** spans any depth', matched against the vault-relative path, case-sensitive). Far exceeds the terse schema descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a precise verb+resource+scope ('List .md file paths in the vault, optionally filtered by folder and/or glob pattern') and immediately bounds the output ('paths only — not content or metadata'). This distinguishes it cleanly from vault_search, vault_search_by_folder and vault_read_note.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit 'When to use' line plus two explicit routing rules: 'Prefer vault_search_by_folder when you need metadata... Prefer vault_search for content-based discovery. Use vault_read_note to read a note from the results.' Names both alternatives and the condition that selects each.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

vault_list_property_keysList Property KeysA
Read-onlyIdempotent

Discover all property keys in the vault with note counts and sample values. Lets you understand the vault's metadata schema without reading individual notes.

Example: vault_list_property_keys() returns [{ key: "tags", count: 342, sample_values: ["session-log", "project"] }, ...]

When to use: Discovering what properties exist before searching by property. Good first step for vault orientation alongside vault_list_tags. Prefer vault_list_property_values when you need the full list of values for a specific key. Prefer vault_search_by_property to find notes matching a specific key-value pair.

Parameters:

  • folder names a whole folder and includes its subfolders: "Projects" covers "Projects/Archive" but not "ProjectsOld/". Matching ignores ASCII letter case; omit folder to scan the entire vault.

Behavior:

  • Only frontmatter properties count; inline Dataview fields (key:: value) are not listed.

  • count is the number of notes that have the key, including notes where its value is empty (null).

  • sample_values are the key's 3 most frequent displayed strings. Values with the same string are grouped before choosing samples, counting each array element separately; ties use binary text order.

  • Checkbox values appear as "1" and "0"; null values are skipped.

Errors:

  • An empty vault or folder returns an empty array, not an error.

Returns: JSON array of { key, count, sample_values } sorted by count descending, then by key.

ParametersJSON Schema
NameRequiredDescriptionDefault
folderNoRestrict to a folder (e.g. "Projects")

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover the safety profile (readOnly, idempotent, non-destructive), and the description adds substantial context beyond that: only frontmatter counts (inline Dataview ignored), count includes null-valued notes, sample_values selection rules and tie-breaking, checkbox rendering as '1'/'0', and the empty-vault behavior. This genuinely enriches understanding rather than repeating annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with purpose and example, then organized under When to use / Parameters / Behavior / Errors / Returns. Long but every section carries needed information, and the header structure makes it skimmable.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, but the description fully specifies the return shape (JSON array of {key, count, sample_values}, sorted by count desc then key) and error behavior. For a single-param read tool, nothing an agent needs is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the single param has a terse schema description, but the description adds real meaning: folder includes subfolders ('Projects' covers 'Projects/Archive' but not 'ProjectsOld/'), matching is ASCII-case-insensitive, and omitting folder scans the whole vault. This goes beyond the schema text, though it is a single parameter.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Discover all property keys in the vault with note counts and sample values') and scopes it as metadata-schema inspection without reading notes. The example concretely anchors the shape. Clearly distinguishable from siblings like vault_list_tags and vault_list_property_values.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit 'When to use' plus two named alternatives with the conditions that select them: vault_list_property_values for values of a specific key, vault_search_by_property for key-value matching. Nothing is left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

vault_list_property_valuesList Property ValuesA
Read-onlyIdempotent

List distinct values for a specific property key with how often each occurs.

Example: vault_list_property_values({ key: "status" }) returns [{ value: "done", count: 211 }, { value: "active", count: 47 }, ...]

When to use: Enumerating possible values for a property key before calling vault_search_by_property. Call vault_list_property_keys first to discover valid key names.

Parameters:

  • key is case-sensitive and must match exactly as returned by vault_list_property_keys.

  • folder names a whole folder and includes its subfolders: "Projects" covers "Projects/Archive" but not "ProjectsOld/". Matching ignores ASCII letter case.

  • limit applies after sorting by count descending, so you always get the most-used values first. Nothing in the response signals truncation: exactly limit values may mean more exist, which is common for keys with many distinct values like "title" or "created"; raise limit to check.

Behavior:

  • Handles both scalar properties (status: "active") and array properties (tags: ["a", "b"]). Array elements are unpacked and counted individually, so the sum of counts may exceed the note count.

  • Values with the same displayed string share one row with combined occurrence counts: number 1 and text "1" count together, while text "1.0" stays separate. Grouping happens before limit.

  • Checkbox values are stored as 1 and 0, so true and false come back as "1" and "0", counted with the numbers 1 and 0.

  • vault_search_by_property matches stored numbers numerically and text exactly; value "1" matches the number 1, the text "1", and a checked checkbox.

  • null values are skipped.

Errors:

  • An unknown key or empty folder returns an empty array, not an error.

Returns: JSON array of { value, count } sorted by count descending, then by value in binary text order ("10" before "2").

ParametersJSON Schema
NameRequiredDescriptionDefault
keyYesProperty key name — use vault_list_property_keys to discover valid keys (e.g. "status", "type", "tags").
limitNoMax values to return (default 50).
folderNoRestrict to a folder (e.g. "Projects")

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover the safety profile (readOnly, idempotent, non-destructive), but the description adds substantial non-obvious behavior: array elements unpacked and counted individually, scalar/array handling, numeric-vs-text grouping quirks ('1' vs '1.0'), checkbox stored as 1/0, null skipping, and the silent-truncation caveat at limit. These are things an agent genuinely could not infer from annotations alone.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with a concrete example, then organized under clear headers (When to use, Parameters, Behavior, Errors, Returns). Every paragraph is dense functional content; nothing is filler or repeated from the schema or annotations.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only listing tool with no output schema, the description fully specifies the return shape, sort order (count descending then binary text order), and error behavior (unknown key or empty folder yields an empty array, not an error). An agent has everything needed to call and interpret it.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, yet the description adds real meaning beyond it: 'key' is case-sensitive and must match exactly, 'folder' includes subfolders with case-insensitive matching ('Projects' covers 'Projects/Archive' but not 'ProjectsOld/'), and 'limit' applies after count-descending sort with no truncation signal. This goes well past restating the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('List distinct values for a specific property key with how often each occurs'), which unambiguously separates it from vault_list_property_keys (keys) and vault_search_by_property (notes). The inline example with the exact response shape removes any ambiguity about output.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states when to use it ('Enumerating possible values ... before calling vault_search_by_property') and the prerequisite sequencing ('Call vault_list_property_keys first to discover valid key names'). The routing to siblings is stated, not inferred.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

vault_list_tagsList TagsA
Read-onlyIdempotent

List all tags in the vault with note counts, ordered by count descending. Only frontmatter tags are counted (inline #tags in note bodies are not indexed). Each hierarchical tag (e.g. "project/vault-cortex") appears as one full entry, not split into segments. Count is unique notes, not occurrences.

Example: vault_list_tags() returns [{ tag: "session-log", count: 42 }, { tag: "project/vault-cortex", count: 8 }, ...]

When to use: Discovering what tags exist before searching by tag. Good first step for vault orientation. Prefer vault_search_by_tag once you know which tag to query — it supports hierarchical prefix matching ("project" matches "project/*").

Errors:

  • A vault with no tagged notes returns an empty array, not an error.

Returns: JSON array of { tag, count }; tag omits the "#" prefix.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover the safety profile (readOnly, idempotent, non-destructive, closed-world), yet the description adds substantial behavioral context beyond them: only frontmatter tags are indexed (inline #tags ignored), hierarchical tags are returned whole rather than split, count is unique notes not occurrences, and the # prefix is stripped. It also discloses the empty-vault behavior (empty array, not an error), which no annotation conveys.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core behavior, then example, usage, errors, and returns, each under a clear label. Every sentence carries new information; even the example earns its place by showing the exact record shape. No filler or hedging.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description carries the full burden of describing the return value, and it does so with both a concrete example and a prose summary ('JSON array of { tag, count }'). Error behavior for the empty case is covered, so an agent has everything needed to call and interpret this tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so there is nothing for the description to disambiguate; the baseline for a no-param tool is 4. It does clarify the shape of each returned entry and the tag format, which is useful context but belongs to output semantics rather than parameter semantics.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('List all tags in the vault with note counts') plus the ordering ('count descending'), which is more than a bare restatement of the title. It also implicitly separates itself from siblings like vault_search_by_tag and vault_list_property_keys by scoping to tags only.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly names when to use it ('Discovering what tags exist before searching by tag. Good first step for vault orientation') and names the alternative with the condition that selects it ('Prefer vault_search_by_tag once you know which tag to query — it supports hierarchical prefix matching'). This is textbook when/when-not/alternative guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

vault_list_tasksList TasksA
Read-onlyIdempotent

List checkbox tasks across the whole vault with structured filters — the Tasks-plugin data model over MCP. Both task metadata formats are indexed: emoji signifiers (📅 due, ⏳ scheduled, 🛫 start, ➕ created, ✅ done, ❌ cancelled, 🔺⏫🔼🔽⏬ priority, 🔁 recurrence, 🏁 onCompletion, 🆔/⛔ dependencies) and Dataview inline fields ([due:: 2026-07-04], [priority:: high], ...). Every result carries its attribution — note path, folder, line number, and the nearest heading when the task sits under one (the lane on a Kanban board) — so no follow-up reads are needed to locate a task. Task lines inside fenced code blocks and %% %% comment blocks are not indexed. Checkboxes typed NON_TASK in the Tasks plugin's status registry are also excluded.

Example: vault_list_tasks({ due: { before: "2026-07-04" } }) — overdue triage; the default status (not_done) and sort (due ascending) make this the "what's overdue?" call Example: vault_list_tasks({ path: "Code Projects/vault-cortex/TASKS.md", heading: ["Active", "Up Next", "Waiting On"], sort_by: "position" }) — actionable Kanban lanes in board order Example: vault_list_tasks({ folder: "Code Projects/vault-cortex" }) — all open tasks across a project tree (TASKS.md + task-notes/ subdirectories) Example: vault_list_tasks({ status: "done", done: { after: "2026-06-26" } }) — what got completed this week Example: vault_list_tasks({ top_level_only: true, path: "TASKS.md" }) — board cards only, excluding checklist sub-items

When to use: Reading task status and order on one board (path + sort_by: "position"; add status: "all" to include done and cancelled cards). Also any vault-wide task triage question — "what's overdue?", "what's open per project?", "what did I finish this week?" — in one call instead of per-board reads. Prefer vault_read_note (heading mode) only when you need a lane's verbatim Markdown or a task's state right after a write. Prefer vault_search for full-text queries over note content.

Behavior: Reads the search index, which picks up a file change within a few seconds, so a task written moments ago may still show its old state.

Parameters:

  • status: virtual values expand in arrays — ["not_done", "done"] matches todo + in_progress + done.

  • due / scheduled / start / done / created / cancelled: a date filter only matches tasks that HAVE that date.

  • folder: a whole folder, subfolders included ("Projects" covers "Projects/Archive" but not "ProjectsOld/"), ignoring ASCII letter case.

Errors:

  • A malformed or calendar-invalid date filter throws with remediation text ("Use YYYY-MM-DD")

  • path without the ".md" extension is rejected

  • No matches returns { total: 0, tasks: [] }, not an error

Returns: JSON { total, tasks }. Every task carries path, line, status, status_char, description, folder, depth (0 for top-level, 1+ for sub-tasks), is_kanban_task, depends_on, and tags (the arrays are [] when empty). Every other field appears only when the task has it: heading (nearest heading above the task), created/scheduled/start/due/done/cancelled dates, priority, recurrence, on_completion, task_id, block_id, parent_block_id (sub-tasks whose parent carries a ^block-id), done_lanes (Kanban boards only), and subtask_progress — { done, total } over the task's DIRECT checklist children, present only when the task has a checklist; done counts status "done" only (a cancelled child counts toward total, not done), and the counts ignore the query's filters — so a filtered or top_level_only read still shows each card's checklist progress.

ParametersJSON Schema
NameRequiredDescriptionDefault
dueNoDue date (📅 / [due:: ]) bounds
tagNoInline task tag, bare name without "#"; parent tags match children
doneNoDone date (✅ / [completion:: ]) bounds
pathNoRestrict to one note (vault-relative path ending ".md", case-sensitive)
limitNoMax results (default 50); total always reports the full match count
startNoStart date (🛫 / [start:: ]) bounds
folderNoRestrict to a folder (e.g. "Code Projects/vault-cortex")
statusNoStatus filter, OR-combined (default "not_done" = todo + in_progress, excluding done and cancelled). "all" includes every status.not_done
createdNoCreated date (➕ / [created:: ]) bounds
headingNoExact heading text or array of headings, OR-combined, case-sensitive (e.g. "Active" or ["Active", "Up Next"])
sort_byNoSort key (default "due"). Date sorts cascade through related fields when the primary is absent (through the rest of due → scheduled → start → created, in that order; done does not cascade); each fallback uses its own natural direction. "position" sorts by file path then line number — the natural order for Kanban boards.due
priorityNoPriority levels, OR-combined; "none" selects tasks with no priority signifier
cancelledNoCancelled date (❌ / [cancelled:: ]) bounds
scheduledNoScheduled date (⏳ / [scheduled:: ]) bounds
sort_directionNoSort direction. Default per field: "asc" for due/scheduled/priority/position, "desc" for start/created/done/note_mtime. Within a date cascade, each fallback uses its own default; an explicit value overrides all fields uniformly.
top_level_onlyNoWhen true, only top-level tasks (depth 0) are returned — excludes indented sub-tasks and checklist items. Default false.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly/idempotent/non-destructive, yet the description adds substantial behavior beyond them: index lag ('a task written moments ago may still show its old state'), explicit exclusions (fenced code blocks, %% %% comments, NON_TASK statuses), and error semantics (malformed dates throw, no-match is not an error). This is exactly the context an agent needs to avoid misreading stale results.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with purpose, then examples, usage, behavior, parameters, errors, returns in clear sections — every section earns its place for a 16-parameter tool. It is long, and some example redundancy exists, but the structure makes it scannable rather than bloated.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, so the description carries the full return-shape burden, and it does so thoroughly (total/tasks, always-present vs conditional fields, subtask_progress semantics including the cancelled-child edge case and filter independence). For a complex 16-param read tool, nothing material is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds genuine meaning beyond it: 'virtual values expand in arrays' for status, the rule that 'a date filter only matches tasks that HAVE that date', and folder case-insensitivity with subfolder inclusion. These semantics are not obvious from the schema alone.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource (list checkbox tasks across the vault) and immediately scopes it against the Tasks-plugin data model, naming what metadata formats are indexed. It explicitly routes away from siblings: 'Prefer vault_read_note (heading mode)... Prefer vault_search for full-text queries'.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Has a dedicated 'When to use' block plus five concrete examples covering triage, Kanban lanes, project trees, and weekly done queries, and explicitly names alternatives (vault_read_note, vault_search) with the condition that selects each. Nothing is left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

vault_memory_recallMemory RecallA
Read-onlyIdempotent

Recall memory entries about a topic — entry-granular hybrid (keyword + semantic) retrieval across ALL About Me/ files and ALL time. Returns every relevant dated entry sorted oldest-first, so the full evolution of a preference, opinion, or fact is visible — semantic matching finds early entries even when their phrasing differs from the query. Tuned for recall over precision: expect some marginal entries and judge relevance yourself when synthesizing an answer. Content-word queries ("testing philosophy", "sustainable pacing") rank best. A meta-framed query ("opinions on testing") that scores below the relevance threshold degrades to relaxed any-term keyword matching instead of returning nothing.

Example: vault_memory_recall({ query: "working hours and pacing" }) Example: vault_memory_recall({ query: "testing philosophy", file: "Opinions" })

When to use: Answering "what does my memory say about X?" or "how has my view on Y evolved?" — topic-based recall across memory files. Prefer vault_get_memory to read a known file or section verbatim; prefer vault_search for notes outside the memory layer.

Errors:

  • No matching entries returns { entries: [], total: 0 }, not an error

  • An unknown file returns empty results — call vault_list_memory_files to discover valid names

Returns: JSON { entries, total, truncated, search_mode, reranked }. Each entry is { file, section, date, text } — text is the raw entry markdown (wikilinks intact, continuation lines included); file and section feed directly into vault_get_memory or vault_delete_memory. entries ascend by date (oldest first). total counts all matched entries; truncated=true means limit dropped the least-relevant matches — never a date range — so raise limit or narrow the query for the complete set. file is applied before limit, so limit keeps the most relevant matches from that file. search_mode is "hybrid" when vector matching contributed, "fts" when the entries came from keyword matching alone — including the any-term fallback that rescues a would-be-empty result; reranked is true when the cross-encoder relevance cut was applied.

ParametersJSON Schema
NameRequiredDescriptionDefault
fileNoOptional: restrict to one memory file, name without .md (e.g. "Opinions"), in its exact letter case. Omit for cross-file recall — the default and usual choice.
limitNoCap on returned entries (default 50).
queryYesTopic to recall — natural language works best (semantic matching bridges phrasing drift across months); content words about the topic rank better than meta framing ("testing philosophy" over "opinions on testing")

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare read-only/idempotent/non-destructive, but the description adds substantial behavioral context beyond them: recall-over-precision tuning (expect marginal entries), the any-term keyword fallback that rescues a would-be-empty result, that unknown files return empty rather than erroring, and the meaning of truncated/search_mode/reranked.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with purpose then usage then errors then returns, so scanning is easy. It is long and the Returns paragraph is dense, but nearly every sentence carries non-redundant semantics; only marginal tightening is possible.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description fully specifies the return shape ({entries, total, truncated, search_mode, reranked}), per-entry fields, sort order, and error semantics. Nothing an agent needs to interpret results correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description genuinely extends it: query phrasing guidance (content words rank better than meta framing), that file is applied before limit, and that limit drops least-relevant matches rather than a date range. The file parameter's case-sensitivity and default omission are covered by the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (recall), resource (memory entries), retrieval mechanism (entry-granular hybrid keyword + semantic), and scope (ALL About Me/ files, ALL time). It explicitly distinguishes itself from vault_get_memory and vault_search, so an agent can route without opening schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The 'When to use' block gives concrete trigger questions ('what does my memory say about X?') and names the two alternatives with the conditions that select them: vault_get_memory for a known file/section verbatim, vault_search for notes outside the memory layer. Exclusions are explicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

vault_move_noteMove NoteA
Destructive

Move or rename a note and rewrite every link across the vault that points to it, like Obsidian's built-in rename. Incoming links in other notes — [[wikilinks]], [[wikilink|aliases]], [[wikilink#headings]], ![[embeds]], markdown, and frontmatter links (e.g. related:) — are updated to the new path; the moved note's own relative links are fixed so they still resolve from the new folder, including relative links to attachments (e.g. ![[../assets/photo.png]], img). A link is only rewritten when leaving it unchanged would break it, so a short [[Note]] that stays unambiguous after a folder move is left alone. Moving a note any other way can silently break its backlinks.

Example: vault_move_note({ old_path: "Inbox/Draft.md", new_path: "Inbox/Spec.md" }) — pure rename. Example: vault_move_note({ old_path: "Inbox/spec.md", new_path: "Inbox/Spec.md" }) — case-only rename; works even where the filesystem treats both spellings as one file. Example: vault_move_note({ old_path: "Inbox/Spec.md", new_path: "Projects/Spec.md" }) — move to another folder, updating links and the note's own relative links. Example: vault_move_note({ old_path: "Inbox/Spec.md", new_path: "Projects/Spec.md", prune_empty_folders: true }) — also remove "Inbox" if the move empties it.

When to use: Renaming a note or relocating it to a different folder while keeping the link graph intact. Prefer this over vault_write_note + vault_delete_note, which would orphan every backlink. To only change a note's body or properties, use vault_patch_note or vault_update_properties. Protected paths (About Me/ and the daily notes folder (read from DAILY_NOTES_FOLDER or .obsidian/daily-notes.json, defaulting to Daily Notes/)) cannot be moved.

Parameters:

  • prune_empty_folders removes each parent folder of old_path that the move leaves with zero entries, up to but never including the vault root; a folder holding any file, even a hidden .DS_Store, is kept. An in-place rename or a move into a subfolder of the source prunes nothing. Pruning is best-effort: a folder that can't be removed never fails the call. Without it, empty folders stay, matching Obsidian.

Errors:

  • "destination exists: …" — a note already lives at new_path; this tool never overwrites. Pick a free path or delete the existing note first.

  • "note not found: …" — old_path does not exist; verify it with vault_list_notes.

  • "source and destination are the same path" — old_path and new_path name the same note, so there is nothing to move.

  • "cannot move protected path …" / "cannot move into protected path …" — old_path or new_path sits under a protected folder.

  • "cannot read daily notes config from .obsidian/daily-notes.json" — the file exists but is unreadable, so the daily notes folder to protect is unknown; repair it, or set DAILY_NOTES_FOLDER or PROTECTED_PATHS, then retry.

  • "path must end in …" — both old_path and new_path must end in .md.

  • "absolute path blocked" / "path traversal blocked" / "hidden path blocked" — use vault-relative paths with no hidden (dot-prefixed) file or folder in them (notes cannot move from or into hidden paths, matching Obsidian).

  • "concurrent write in progress" — a write is in flight on the note, the destination, or one of its backlink sources (the move locks all of them as one unit); retry the move.

  • "backlink set did not stabilize" — the vault was modified during the move and new backlink sources kept appearing across retries; nothing was written; retry the move.

  • An ordinary move that fails partway (rare: a permission or disk error) — no data is lost, and the error names what failed and the resulting state. The original is deleted last, after the destination and every backlink are written. If a backlink write failed: new_path exists and old_path is intact, so delete the partial new_path, then re-run the move. If the final delete failed: both paths exist, so delete old_path to finish.

  • A case-only rename that fails partway — the note is renamed in place first. If the rename failed: nothing was written. If a later link write failed: the note already lives at new_path and old_path is gone, so fix the remaining links in place (the error names the note whose update failed) instead of re-running the move.

Obsidian syntax: Link rewrites preserve each link's existing form — embed marker (!), heading anchor (#…), and alias (|…) are kept; a markdown link keeps its original extension and link text. Only the target path is changed.

Returns: JSON with moved_to (the new path), links_updated (count of link occurrences rewritten, including the moved note's own relative links), updated_notes (sorted paths of the other notes that were edited; the moved note is implied by moved_to), and pruned_empty_folders (count of empty parent folders removed — 0 unless prune_empty_folders was set).

ParametersJSON Schema
NameRequiredDescriptionDefault
new_pathYesDestination vault-relative path (e.g. "Projects/Spec.md"). Must end in .md and must not already exist; parent folders are created as needed. Use the exact letter case of existing folders; a different case can create a second folder.
old_pathYesCurrent vault-relative path of the note to move (e.g. "Inbox/Draft.md"). Must end in .md. Use the exact letter case.
prune_empty_foldersNoWhen true, also remove parent folders of old_path that the move leaves empty. Default false.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true and idempotentHint=false, but the description adds far more: exactly which link forms are rewritten, the 'only rewrite when leaving it unchanged would break it' rule, write ordering (original deleted last), partial-failure recovery for both ordinary and case-only renames, lock scope, and pruning semantics. This is behavioral context an agent cannot get from the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core behavior, then examples, when-to-use, parameters, errors, syntax, and returns — clearly sectioned and skimmable. It is long, but the length is justified by the number of distinct error/recovery paths; the four examples earn their place by disambiguating rename vs move vs prune.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Despite having no output schema, the description fully specifies the return payload (moved_to, links_updated, updated_notes, pruned_empty_folders), enumerates the failure modes and their recovery, and covers the preconditions (protected paths, path form). Nothing an agent needs to invoke or recover from this call is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the 'Parameters:' section meaningfully extends prune_empty_folders beyond the schema (per-parent behavior, hidden-file caveat, no-op on in-place rename or moves into source subfolders, best-effort failure tolerance). old_path/new_path behavior such as case-only renames and the .md requirement is also elaborated, though much of that duplicates the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening sentence states a specific verb (move or rename), a specific resource (a note), and the distinctive side effect (rewriting every incoming and outgoing link across the vault). It explicitly distinguishes itself from siblings by naming vault_write_note + vault_delete_note, vault_patch_note, and vault_update_properties as the wrong tools for adjacent tasks.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

A dedicated 'When to use' line gives the triggering scenario (rename or relocate while keeping the link graph intact), and the description states both when-not (use vault_patch_note/vault_update_properties for body or property edits) and the anti-pattern (write+delete orphans backlinks). It also discloses the protected-path precondition that blocks the call.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

vault_patch_notePatch NoteA
Destructive

Surgical edits to a markdown note — append, prepend, replace, or insert content by heading. Frontmatter values are preserved; YAML formatting may be normalized to block style on first edit.

Example: vault_patch_note({ path: "Projects/plan.md", operation: "append", heading: "Open questions", content: "- Which region hosts the backup?" })

When to use: Modifying part of an existing note without overwriting the entire body. Prefer vault_write_note for creating new notes, or full rewrites (with overwrite: true). Prefer vault_replace_in_note for in-place text changes (typos, renaming) that stay in the same location. Prefer vault_create_task for adding a task. Prefer vault_update_task for completing or moving a task in one write.

Operations:

  • append: add content at end of section (or end of file if no heading)

  • prepend: add content after heading line (or at the top of the body, below frontmatter, if no heading — how you add a leading callout). To start a new section above the note's current first heading, use insert_before on that heading, not a no-heading prepend.

  • replace: replace section body (heading preserved; requires heading; errors if the target has child headings unless include_children is set)

  • insert_before: insert content above the heading line (requires heading)

Heading-targeted ops keep the matched heading and write content verbatim. No separator is added around the content — end it with a newline to leave a blank line after the inserted block.

Limitation: A no-heading prepend inserts at body line 0. If the note has content above its first heading and your content starts with a heading, the pre-existing content becomes the new section's body. The write still succeeds and the confirmation says so — use insert_before on the first heading to place a section above it instead.

Section boundaries: a section spans from its heading to the next heading of the same or higher level (or EOF), so it includes its child headings. Empty headings ("##" with no text) act as boundaries but cannot be targeted — edit their content via vault_replace_in_note instead.

Editing a leading callout: read it via vault_read_note(outline: true), then vault_replace_in_note the old block for the new one (a no-heading prepend would stack a second callout above it).

Errors:

  • "note not found" — path does not exist; check vault_list_notes for valid paths

  • "path must end in …" — add the .md extension

  • "heading not found" — no heading matches the text; error lists available headings

  • "ambiguous heading" — multiple headings match; use heading_level to disambiguate, or use vault_replace_in_note to target by text content when headings share the same level

  • "operation … requires a heading target" — replace and insert_before need a heading

  • "heading cannot be empty" — heading is whitespace only; pass the heading's text

  • "content begins with the heading … which would duplicate it" — content's first line repeats the target heading; omit it (the matched heading is kept automatically)

  • "section … has N child headings …" — the target section contains child headings that replace would destroy; pass include_children: true to confirm, or target the child heading directly

  • "absolute path blocked" / "path traversal blocked" / "hidden path blocked" — use a vault-relative path with no hidden (dot-prefixed) file or folder in it

  • "concurrent write in progress" — another write to this note is in flight; re-read the note and retry

  • "content contains a control character" — content includes a non-printable control byte; remove it before writing

Obsidian syntax: Content is Obsidian Flavored Markdown (no escaping applied). Watch for: #word = tag, [[ = wikilink, %% = comment block. Inserting heading-level content (## New Section) changes the note's structure — future heading-targeted ops may resolve differently. Table rows: send only the data row ("| cell1 | cell2 |"), not the header or separator — duplicating them splits the table.

Returns: Confirmation message — "Applied to → ", where target is the matched heading (e.g. "## Active") or "file body" for a no-heading append/prepend. A no-heading prepend that nested existing content under an inserted heading adds a sentence naming the content's size and the call that would have avoided it.

ParametersJSON Schema
NameRequiredDescriptionDefault
pathYesVault-relative path to the note, including the ".md" extension (e.g. "TASKS.md", "Projects/plan.md"). Use the exact letter case.
contentYesMarkdown content to insert. Must not begin with the target heading text (it would duplicate the heading, which is kept automatically).
headingNoTarget heading text (case-sensitive exact match). Omit for a file-level append or prepend.
operationYesappend | prepend | replace | insert_before.
heading_levelNoHeading level (1-6) for disambiguation when multiple headings share the same text
include_childrenNoWhen true, allows replace to overwrite a section that contains child headings. Without this, replace errors if children exist — preventing silent data loss.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true and non-idempotent, but the description goes far beyond them: frontmatter preservation and YAML normalization to block style, no separator insertion around content, the data-loss risk of replace on sections with child headings, the no-heading prepend nesting trap, section-boundary semantics, empty-heading non-targetability, and a full error catalogue with recovery actions.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose and when-to-use are front-loaded and the body is cleanly partitioned into Operations, Limitation, Section boundaries, Errors, Obsidian syntax, and Returns. It is long, and the 13-entry error list plus the table-row and Obsidian-syntax asides push past what most calls need, but nearly every line carries actionable information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a destructive, non-idempotent mutation tool with no output schema, the description covers safety, failure modes, structural side effects, and the exact return string format. Nothing an agent needs to invoke it correctly or recover from errors is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds real meaning beyond the enum: per-operation targeting rules (which ops require a heading), how heading matching behaves, how heading_level disambiguates, and what include_children actually authorizes. It stops short of documenting path/content syntax further because the schema already does that.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Surgical edits to a markdown note') and enumerates the four operations up front. It explicitly distinguishes itself from siblings by naming vault_write_note, vault_replace_in_note, vault_create_task, and vault_update_task with the condition that selects each.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is an explicit 'When to use' line plus four named alternatives with the scenario each covers (new notes, full rewrites, in-place typo fixes, task creation/completion). Edge cases such as 'to start a new section above the first heading use insert_before, not a no-heading prepend' make the routing unambiguous.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

vault_read_fileRead FileA
Read-onlyIdempotent

Read a non-markdown vault file in its most useful form per type — the read-side companion to vault_read_note for everything that isn't a note.

Example: vault_read_file({ path: "attachments/diagram.png" }) — the image itself, shrunk when too large Example: vault_read_file({ path: "Boards/Roadmap.canvas" }) — a readable outline of the canvas Example: vault_read_file({ path: "exports/data.json" }) — the file content as text Example: vault_read_file({ path: "exports/big.csv", limit: 500 }) — the first 500 lines, preceded by a metadata line stating the window and total line count Example: vault_read_file({ path: "papers/research.pdf" }) — structured text with title, headings, and links

What each file type returns:

  • Images (.png/.jpg/.jpeg/.gif/.webp): the image as a viewable image block — downscaled and recompressed server-side when it exceeds the image output budget (MAX_IMAGE_OUTPUT_BYTES, 49152 bytes) or 1568 pixels on its longer side, delivered untouched otherwise — plus a text line stating the path, delivered format/dimensions/bytes, and the original dimensions when shrunk. Animated GIFs are reduced to their first frame when recompressed.

  • Canvas (.canvas): a readable markdown outline per JSON Canvas 1.0 — groups (by visual containment), node content in reading order, and a connections list with edge labels.

  • PDFs (.pdf): structured text with document metadata — title, page count, heading hierarchy (from font sizes relative to the body text), code blocks and inline code (from monospace fonts), page separators, and a deduplicated links footer. Richer than flat text extraction: headings, code, and hyperlinks that flat extraction loses are preserved.

  • Text formats (.svg/.json/.txt/.csv/.xml/.log/.yaml/.yml/.base): the file content verbatim as text. .svg is returned as its XML source; .base as its YAML source.

raw: true switches a canvas or PDF to its other form:

  • Canvas: the exact JSON source (geometry, ids, colors — full fidelity) instead of the outline.

  • PDF: each page rendered and returned as an image block instead of extracted text, showing layout, diagrams, tables, and formatting that text extraction cannot preserve. Image-only and scanned PDFs work in raw mode. Only the first 5 pages are rendered; the text read (without raw) covers every page.

Line paging: start_line and limit page any text result — text formats, canvas outlines and raw JSON, PDF-extracted text — as a 1-based line window, preceded by a metadata line stating the window, the total line count, and where to continue ("data.csv — lines 51–100 of 400 (continue with start_line: 101)"). The text output cap (100 KiB) applies to each window, so one very long line can still overflow it; paging never gets around the file-size cap. Paged windows come back with \n line endings and no trailing newline; a read without paging inputs stays byte-exact.

When to use: whenever a note references a file you need to actually see or read — an embedded diagram, a linked canvas, data file, or PDF. Find the files a note links to (with byte sizes) via vault_get_outgoing_links; browse a folder's files via vault_list_files. vault_search also indexes canvas, PDF, and text-format content, but not images or other files. For .md notes use vault_read_note — this tool rejects them. To check a large file's line count before reading it whole, request start_line: 1 with limit: 1 — one line plus the total.

Errors:

  • "not a file" — the path ends in .md; read notes with vault_read_note

  • "file not found" — nothing exists at that path; discover valid paths via vault_list_files

  • "absolute path blocked" / "path traversal blocked" / "hidden path blocked" — use a vault-relative path with no hidden (dot-prefixed) file or folder in it (hidden files are not readable, matching Obsidian)

  • "file too large" — the file exceeds the file-size cap (MAX_FILE_BYTES, default 50 MiB)

  • "text output too large" — a text file, canvas, or PDF renders past the text output cap; page it with start_line and limit, or reduce limit when a single window overflows

  • "start line past the end" — start_line exceeds the file's line count; the error states the total, so retry with a smaller start_line

  • "line range is not available" (start_line or limit on an image, or on a PDF read with raw: true) / "raw source is not available for images" (raw on an image) — drop that input; paging applies only to text results, and an image always comes back as its image block

  • "not valid UTF-8" — the file's bytes aren't UTF-8 text; returning them would silently corrupt the content

  • "invalid .canvas JSON" — the canvas file is empty or not valid JSON, so no outline can be built; set raw: true to read its source as text

  • "PDF has no extractable text" — the PDF contains no text (scanned or image-only); the error states the page count. Set raw: true to render pages as images instead

  • "PDF page rendering failed" — raw: true was set but no pages could be rendered; the PDF may be corrupt

  • "image cannot be fitted" — the image could not be compressed under the image output budget

  • an image that cannot be decoded (corrupt, empty, or not an image despite its extension) fails with the decoder's message, e.g. "Input buffer contains unsupported image format"; replace or re-export the file

  • unsupported types (audio, archives, …) return an error naming the readable types plus the file's existence and size

Returns: for images, an image content block plus a one-line metadata text block; for PDFs with raw: true, a metadata text block followed by alternating image and text blocks (one pair per page); for every other supported type, a single text content block — preceded by a window-metadata text block when start_line or limit was given.

ParametersJSON Schema
NameRequiredDescriptionDefault
rawNoReturn the file's alternative form: JSON source for .canvas, page images for .pdf. Changes nothing for text formats, which already return their source. Rejected for images.
pathYesVault-relative path to the file, including its extension (e.g. "attachments/photo.png", "Boards/Roadmap.canvas"). Must NOT end in ".md" — notes are read with vault_read_note. Use the exact letter case.
limitNoMaximum lines returned (default: all remaining).
start_lineNoFirst line to return, 1-based (default 1).

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations cover the safety profile (read-only, idempotent, non-destructive), and the description layers on rich behavior: server-side downscaling thresholds (49152 bytes / 1568px), animated GIF first-frame reduction, the 5-page raw PDF render limit, per-window text caps, and byte-exactness of unpaged reads. This is well beyond what structured fields provide.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with examples, then a per-type return table, paging rules, usage, errors, and returns — logically ordered and each section scannable. However it is unusually long for a read tool, and the exhaustive error catalogue (13 entries) is closer to reference documentation than agent-facing guidance.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description fully compensates by enumerating return shapes per type (image block + metadata line, alternating image/text page pairs, single text block with optional window metadata). Combined with paging, error, and size-cap coverage, an agent has everything needed to call and interpret this tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so baseline is 3; the description adds genuine meaning on top — how start_line/limit produce a metadata prefix with window and total, the 'continue with start_line: 101' hint, and the trick of start_line:1 with limit:1 to probe line count. It slightly understates raw's rejection for images in the paging context, but coverage is strong.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('read a non-markdown vault file') and immediately positions itself as the read-side companion to vault_read_note, explicitly rejecting .md. The per-type return breakdown makes the scope unambiguous relative to siblings like vault_read_note and vault_list_files.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Has a dedicated 'When to use' section naming triggers (a note references a file you need to see), plus explicit alternatives: vault_get_outgoing_links to find linked files, vault_list_files to browse, vault_search for indexed content, vault_read_note for .md. It even states exclusions ('not images', 'this tool rejects them').

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

vault_read_noteRead NoteA
Read-onlyIdempotent

Read a markdown note by its vault-relative path. By default returns the full raw content including properties; optional modes return just the properties, just the heading outline, or just one section — so large notes don't blow the token budget.

Example: vault_read_note({ path: "Projects/vault-cortex.md" }) Example: vault_read_note({ path: "Projects/vault-cortex.md", properties_only: true }) Example: vault_read_note({ path: "TASKS.md", outline: true }) Example: vault_read_note({ path: "TASKS.md", heading: "Active" }) Example: vault_read_note({ path: "TASKS.md", heading: "Done", heading_level: 2 }) // disambiguate when several "Done" headings exist Example: vault_read_note({ path: "TASKS.md", heading: "Done", start_line: 1, limit: 20 }) // first 20 lines of an oversized section

When to use: You know the exact path and need a specific note's content. For a large note (a long board or doc), use outline: true to see its headings and any text sitting above them, then heading: "..." to read just the one section you need — both far cheaper than pulling the whole file. Use properties_only: true when you only need properties. For an oversized note or section, page it with start_line and limit to read a window at a time. To check a note's or section's line count, request start_line: 1 with limit: 1 — one line plus the total. Prefer vault_search when you don't know the path. For task status or order on a board, prefer vault_list_tasks; heading mode returns a lane's verbatim Markdown. Prefer vault_get_memory for About Me/ files (returns content without properties). To edit a section you've read, use vault_patch_note. To explore what links to this note or what it links to, use vault_get_backlinks and vault_get_outgoing_links.

Section boundaries: a section spans from its heading to the next heading of the same or higher level (or EOF). Child headings are included. Modes are mutually exclusive — set at most one of properties_only, outline, or heading. Paged reads normalize line endings to LF; unpaged reads stay byte-identical.

Errors:

  • "note not found" — no note exists at this path; verify it with vault_list_notes

  • "heading not found" — no heading matches the text; error lists available headings

  • "ambiguous heading" — multiple headings match; use heading_level to disambiguate, or read the full note (omit heading) when headings share the same level

  • "outline, heading, and properties_only are mutually exclusive" — only one mode per call

  • "heading_level requires a heading" — heading_level only disambiguates a heading; pass heading with it

  • "heading cannot be empty" — heading is whitespace only; pass the heading's text

  • "line paging is not available in outline mode" / "... properties_only mode" — start_line/limit only work on text renditions (full read or heading section)

  • "start line past the end" — start_line exceeds the rendition's line count; error states the total

  • 'path must end in ".md"' — the path names a non-markdown file; read files (images, .canvas, data files) with vault_read_file instead

  • "absolute path blocked" / "path traversal blocked" / "hidden path blocked" — use a vault-relative path with no hidden (dot-prefixed) file or folder in it

Returns: Raw markdown string (default); JSON object of properties (properties_only); JSON outline object, shaped as the outline parameter describes (outline); raw markdown of the section, heading line included (heading). When start_line or limit is given, the result is preceded by a window-metadata text block ("path — lines 1–20 of 250 (continue with start_line: 21)").

Outline: bytes at the root is the whole file's on-disk size and modified is its filesystem modification time; each heading's bytes is the exact UTF-8 byte length of the text that heading mode returns for that section. Empty headings ("##" with no text) appear with text: "" — they act as section boundaries but cannot be targeted by the heading parameter; read the parent section (which includes child headings) or the full note, and edit via vault_replace_in_note.

ParametersJSON Schema
NameRequiredDescriptionDefault
pathYesVault-relative path to the note, including the ".md" extension (e.g. "About Me/Principles.md"). Use the exact letter case.
limitNoMaximum lines returned (default: all remaining).
headingNoReturn only this section. Case-sensitive exact match.
outlineNoIf true, returns { bytes, modified, leading_callout?, leading_content?, headings } as JSON instead of body content — a cheap structure fetch for large notes. headings: [{ level, text, bytes }]; leading_callout: { type, title, body } when the note has a top-of-file callout; leading_content: the rest of the body text above the first heading (callout lines excluded) when the note has any.
start_lineNoFirst line to return, 1-based (default 1). Pages the text output (full body or a heading section). Not valid for outline or properties_only (JSON modes).
heading_levelNoHeading level (1-6) for disambiguation when multiple headings share the same text; only applies with heading
properties_onlyNoIf true, returns parsed properties as JSON instead of full note content

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover the safety profile (readOnly, idempotent, non-destructive, closed-world), and the description adds substantial non-obvious behavior: mode mutual exclusivity, LF normalization on paged reads vs byte-identical unpaged reads, section-boundary rules, security blocks on absolute/traversal/hidden paths, and a full error catalog. This is well beyond what the annotations convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with purpose then examples, and nearly every sentence earns its place given seven interacting parameters. Slightly long: the Returns and Outline paragraphs restate shape details already present in the schema descriptions, and the six examples are more than strictly needed.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description carries the full return-value burden and does so: raw markdown default, JSON for properties_only and outline, section markdown with heading line included, and the window-metadata prefix on paged reads. Error recovery paths are also covered, so nothing needed to call this correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% (baseline 3), but the description adds semantics the schema cannot express: properties_only/outline/heading are mutually exclusive, heading_level only applies with heading and exists to disambiguate same-text headings, start_line/limit are invalid in JSON modes, and the 'start_line: 1, limit: 1' trick to obtain line counts.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource (read a markdown note) scoped by vault-relative path, and immediately distinguishes itself from vault_read_file via the '.md' requirement and from vault_search via 'you know the exact path'. An agent can tell it apart from every sibling without opening a schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit routing: vault_search when the path is unknown, vault_list_tasks for task status/order, vault_get_memory for About Me files, vault_patch_note to edit, vault_get_backlinks/vault_get_outgoing_links for link exploration. It also gives mode-selection guidance (outline first, then heading; properties_only when only properties are needed) and paging advice for oversized notes.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

vault_recent_notesRecent NotesA
Read-onlyIdempotent

List recently modified or created notes, sorted by timestamp — a time-ordered window into the vault, not a date-range filter.

Example: vault_recent_notes({ sort_by: "modified", limit: 10 }) Example: vault_recent_notes({ sort_by: "created", limit: 5 })

When to use: Catching up on vault changes, finding recent work, or orienting after a break. Prefer vault_search for content-based discovery. Prefer vault_search_by_folder for browsing a specific folder.

Parameters:

  • sort_by + limit interact: "modified" (default) uses filesystem mtime, so every note has a value and limit works predictably. "created" uses the frontmatter created property — notes without it sort last (not excluded), so a small limit may return only notes that have the property; increase limit or use "modified" for broader coverage.

  • "modified" includes any file write (content edits, property changes, sync touches), so recently-synced notes appear recent even without user edits.

Errors:

  • An empty vault returns an empty array, not an error.

Returns: JSON array of note metadata (path, title, tags, related, folder, type, created, modified, bytes, leading_callout?, additional_properties), sorted descending by chosen timestamp. created is null when the property is missing; bytes is on-disk file size.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax results (default 20, no upper cap)
sort_byNoSort order (default "modified")modified

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already mark this read-only and idempotent, and the description adds valuable behavior beyond that: 'created' sorts notes without the property last rather than excluding them, 'modified' catches sync touches and property changes, and an empty vault returns an empty array. These behavioral details will help an agent predict results accurately.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is organized into clear sections: overview, examples, when to use, parameter interactions, errors, and return format. It is longer than a one-liner, but every sentence adds needed operational context, and the core purpose is front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Despite having no output schema, the description fully documents the return shape: JSON array of note metadata with field names and nullability. It also covers empty-vault behavior and parameter edge cases, so an agent has enough information to call and interpret the tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Although the input schema already covers parameter names and defaults, the description explains how sort_by and limit interact, including the pitfall that a small limit with sort_by 'created' may return only notes that have the property. This adds important meaning beyond the schema's minimal descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: 'List recently modified or created notes, sorted by timestamp.' It also distinguishes itself from a date-range filter and shows examples, making the tool's purpose unambiguous and distinct from sibling search tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description includes a dedicated 'When to use' section and explicitly names alternatives: 'Prefer vault_search for content-based discovery. Prefer vault_search_by_folder for browsing a specific folder.' This gives clear routing guidance and conditions for choosing this tool versus siblings.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

vault_replace_in_noteReplace in NoteA
Destructive

Find and replace text in a markdown note's body. Matches exact text (case-sensitive). Properties are preserved; YAML formatting may be normalized to block style on first edit. Operates on the body only — properties must be edited via vault_update_properties or vault_write_note's properties parameter.

Example: vault_replace_in_note({ path: "Projects/plan.md", old_text: "TODO: write summary", new_text: "Summary complete." }) Example: vault_replace_in_note({ path: "Projects/plan.md", old_text: "- [ ] draft outline\n", new_text: "" }) — removes the whole line, line break included.

When to use: Targeted text changes within a single location — fixing typos, updating values, renaming terms, or removing a short line (new_text=""). Replaces text in place; does not move content across sections. To delete a large multi-line block, prefer vault_delete_span (short anchors instead of full old_text). To replace a large block by anchors instead of reproducing the full old_text, use vault_replace_span. To relocate content between headings, use vault_patch_note to add at the target first, then remove from source (new_text="") — add-before-delete, so a failure duplicates the block instead of losing it.

Parameters:

  • old_text: include enough surrounding context to ensure uniqueness when the target text appears in multiple places. No regex.

  • new_text: non-empty new_text replaces the match exactly. A deletion (new_text="") that leaves an empty line where the match was joins the empty lines above and below it into one gap that keeps the larger of the two counts (only at the end of the note, the count above drops by one); where matches empty their lines, the gap keeps at least one empty line unless it ends the note, so include the line break in old_text to remove the line. Any other deletion, such as text inside a line or a line break that joins two lines, is written exactly as asked. Empty lines outside the joined gaps never change. A line holding only spaces or tabs counts as text, not as an empty line.

  • replace_all_occurrences: replacing only the first match is a safety default for when old_text appears in multiple places. Set true for deliberate bulk renames or term replacements.

Errors:

  • "note not found" — path does not exist; check vault_list_notes for valid paths

  • "path must end in …" — add the .md extension

  • "text not found" — old_text does not appear in the note body; verify exact text with vault_read_note

  • "absolute path blocked" / "path traversal blocked" / "hidden path blocked" — use a vault-relative path with no hidden (dot-prefixed) file or folder in it

  • "concurrent write in progress" — another write to this note is in flight; re-read the note and retry

  • "new_text contains a control character" — new_text includes a non-printable control byte; remove it before writing

Obsidian syntax: new_text is Obsidian Flavored Markdown (no escaping applied). Watch for: #word = tag, [[ = wikilink, %% = comment block in replacement text.

Returns: Confirmation message with replacement count (number of occurrences replaced).

ParametersJSON Schema
NameRequiredDescriptionDefault
pathYesVault-relative path to the note, including the ".md" extension (e.g. "Projects/plan.md"). Use the exact letter case.
new_textYesReplacement text. Empty string ("") deletes the matched text.
old_textYesExact text to find (case-sensitive). Matches in the body only — text inside frontmatter properties is not searched.
replace_all_occurrencesNoReplace all occurrences (default: false — replaces first occurrence only)

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already flag destructiveHint=true and idempotentHint=false, but the description adds substantial context beyond them: properties are preserved, YAML may be normalized to block style, edits are body-only, replace_all defaults to first-match for safety, and a full error catalogue (concurrent writes, blocked paths, control characters) is enumerated.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with purpose, then examples, when-to-use, parameters, errors — a logical order. However, the paragraph explaining empty-line joining on deletion is dense and hard to parse, and could be tightened given how narrow the case is.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a destructive, non-idempotent mutation tool with no output schema, the definition covers scope boundaries, side effects, safety defaults, error recovery paths, and the return value (confirmation with replacement count). Nothing an agent needs to call it safely is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% (baseline 3), yet the description adds real meaning: old_text requires enough context for uniqueness and is not regex; new_text is Obsidian Flavored Markdown with no escaping, with tag/wikilink/comment caveats; and the long empty-line deletion semantics and replace_all rationale go well beyond the schema's one-line defaults.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Find and replace text in a markdown note's body') and immediately scopes it (body only, not properties). It explicitly distinguishes itself from vault_replace_span, vault_delete_span, and vault_patch_note, so an agent can route without opening schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides an explicit 'When to use' section with concrete scenarios (typos, renaming, deleting a short line) and names three alternatives with the precise conditions that select each (large multi-line block → vault_delete_span; anchor-based block replace → vault_replace_span; relocation between headings → vault_patch_note).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

vault_replace_spanReplace SpanA
Destructive

Replace a contiguous block of whole lines in a note's body with new content, identified by short anchor substrings instead of the block's full text. Each anchor locates a full line — the entire line is selected, not just the matching substring. Case-sensitive matching. Properties are preserved; YAML formatting may be normalized to block style on first edit. Operates on the body only.

Example: vault_replace_span({ path: "Tracker.md", start_anchor: "| 2024-03-02 | Acme", content: "| 2024-03-02 | Acme Corp | Updated |" }) — replaces the one table row whose line contains that fragment. Example: vault_replace_span({ path: "Notes/Plan.md", start_anchor: "> [!warning] Stale", end_anchor: "remove after launch", content: "> [!info] Current\n> Updated for v2." }) — replaces the callout block with a new one.

When to use: Replacing a block you have already read — a table row, callout, or run of list items — where reproducing it exactly as old_text would be error-prone. Pick a short, unique fragment of the first line for start_anchor and, for a multi-line block, the last line for end_anchor. Prefer vault_replace_in_note for small in-place text changes (typos, renaming). Prefer vault_delete_span when removing without replacement.

Parameters:

  • end_anchor is searched at or after the start line, so the span can never run backward; it must be unique among those lines. If both match the same line, only that one line is replaced.

  • content: empty lines at its start and end join the empty lines around the replaced lines, and each joined gap keeps the larger of the two counts (only at the end of the note, the count above drops by one). So content can widen a gap but not narrow it: a trailing newline leaves at least one empty line after the new block unless the block ends the note. Content made only of empty lines joins both sides into one gap. Empty lines inside content are written as given, and no other empty line in the note changes. A line holding only spaces or tabs counts as text, not as an empty line.

  • first_match applies to both anchors independently.

Errors:

  • "note not found" — verify path with vault_list_notes

  • "path must end in …" — add the .md extension

  • "start anchor not found" / "end anchor not found" — no line contains the fragment (for end_anchor, none at or after the start line); verify with vault_read_note

  • "ambiguous start anchor …" / "ambiguous end anchor …" — the anchor matches multiple lines; use a longer fragment or set first_match: true

  • "absolute path blocked" / "path traversal blocked" / "hidden path blocked" — use a vault-relative path with no hidden (dot-prefixed) file or folder in it

  • "concurrent write in progress" — another write to this note is in flight; re-read the note and retry

  • "content contains a control character" — content includes a non-printable control byte; remove it before writing

Obsidian syntax: content is Obsidian Flavored Markdown (no escaping applied). Watch for: #word = tag, [[ = wikilink, %% = comment block.

Returns: Confirmation message "Replaced lines with lines in " — N counts the lines the span covered, M the line breaks in content plus one.

ParametersJSON Schema
NameRequiredDescriptionDefault
pathYesVault-relative path to the note, including the ".md" extension (e.g. "Tracker.md", "Notes/Plan.md"). Use the exact letter case.
contentYesReplacement content (one or more lines) — replaces every line of the matched span. Must be non-empty.
end_anchorNoShort substring that identifies the LAST line of the block. The entire line is selected. Omit to replace just the single line containing start_anchor.
first_matchNoIf an anchor matches more than one line, use the first match instead of erroring (default: false — ambiguity is an error).
start_anchorYesShort, unique substring that identifies the first line of the block (case-sensitive). The entire line is selected, not just the substring. Pick a brief fragment — do not paste the whole block.

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare destructiveHint/readOnlyHint/idempotentHint; the description goes far beyond by disclosing whole-line selection semantics, case sensitivity, property preservation, YAML normalization to block style, body-only scope, and concurrency behavior ('concurrent write in progress'). It even documents the full error surface and Obsidian syntax pitfalls, none of which structured fields convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well front-loaded (purpose, examples, when-to-use, parameters, errors, returns) with clear section labels, and the length is largely justified by the operation's complexity. However, the empty-line joining rules are convoluted and slightly redundant with the Returns section, so it is not maximally tight.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a destructive 5-parameter tool with no output schema, the description covers everything needed: return format with N/M semantics, the complete error catalog with remediation, and Obsidian syntax hazards. Nothing an agent needs to invoke it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% (baseline 3), but the description adds genuinely new semantics: end_anchor is searched at or after the start line so spans cannot run backward, first_match applies to both anchors independently, and content empty-line behavior is spelled out in detail. This meaningfully exceeds the schema's own parameter text.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The first sentence names a specific verb and resource ('Replace a contiguous block of whole lines in a note's body') and immediately states the distinguishing mechanism (anchor substrings rather than full text). It explicitly contrasts with vault_replace_in_note and vault_delete_span, so an agent can select it without opening sibling schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

A dedicated 'When to use' paragraph describes the exact scenario (a block already read, where reproducing old_text would be error-prone) and names two alternatives with their selecting conditions. It also gives practical anchor-selection guidance (short unique fragment of first line, last line for end_anchor).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

vault_search_by_folderSearch by FolderA
Read-onlyIdempotent

Browse notes in a folder with full metadata (tags, type, related, created, modified) — unlike vault_list_notes, which returns paths only.

Example: vault_search_by_folder({ folder: "Projects" }) or vault_search_by_folder({ folder: "About Me", recursive: false })

When to use: Exploring a folder's contents with full context for vault orientation. Prefer vault_list_notes when you only need paths. Prefer vault_search when you have a text query. Use vault_get_backlinks or vault_get_outgoing_links to explore how notes in a folder connect to the rest of the vault.

Parameters:

  • folder names a whole folder, not a text prefix: "Projects" matches notes under "Projects/" but not "ProjectsOld/". Matching ignores ASCII letter case, and a trailing slash is ignored.

  • limit applies after sorting, so you get the most recently modified notes. Nothing in the response signals truncation: exactly limit results may mean more exist, so raise limit to check.

Behavior: Reads the search index, which picks up a file change within a few seconds, so a note written moments ago may not appear yet. Notes in hidden (dot-prefixed) folders are never indexed, so never appear.

Errors:

  • An empty or nonexistent folder returns an empty array, not an error.

Returns: JSON array of note metadata sorted by most recently modified, then by path: path, title, tags, related, folder, type, created (frontmatter; null when missing), modified (file time), bytes (on-disk size), plus, when present, leading_callout (the note's opening callout, { type, title, body }) and additional_properties (other frontmatter keys).

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax results (default 20)
folderYesFolder path (e.g. "Projects", "About Me")
recursiveNoInclude subfolders (default: true); false lists only the folder's top level

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the readOnly/idempotent annotations, it discloses index lag ('a few seconds'), that hidden dot-folders are never indexed, that truncation is not signaled ('exactly limit results may mean more exist'), result sort order, and that a nonexistent folder returns an empty array rather than an error. This is rich, non-obvious behavioral context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with purpose, then a contrast, then when-to-use, parameters, behavior, errors, and returns — a clean scaffold. It is longer than strictly minimal, and the Returns field list is dense, but nearly every sentence carries information an agent needs.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description fully covers the return shape (fields, sort order, nullable created, conditional leading_callout/additional_properties), error behavior, and freshness caveats. Nothing needed to call or interpret the tool is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is already 100%, but the description adds real semantics: folder matches a whole folder not a prefix ('Projects' not 'ProjectsOld/'), case-insensitive ASCII matching, trailing slash ignored, and that limit applies after sorting with no truncation indicator. These are behaviors the schema cannot express.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (browse/search) and resource (notes in a folder) with scope, and explicitly contrasts with vault_list_notes which 'returns paths only.' An agent can distinguish it from siblings without opening a schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit when-to-use guidance ('Exploring a folder's contents with full context') plus named alternatives: vault_list_notes for paths, vault_search for text queries, and the backlink/outgoing-link tools for graph traversal. Routing is unambiguous.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

vault_search_by_propertySearch by PropertyA
Read-onlyIdempotent

Find notes where a frontmatter property matches a value — metadata-only search, no text query needed. Handles both scalar properties (status: "active") and array properties (tags, related): for arrays, matches if any element equals the value (contains check, not exact array match).

Example: vault_search_by_property({ key: "status", value: "in-progress" }) Example: vault_search_by_property({ key: "type", value: "session-log", folder: "Code Projects" })

When to use: Finding notes by metadata when you don't have a text query. Prefer vault_search when you also have a text query (it supports property filters too). Prefer vault_search_by_tag for tag-specific queries (supports hierarchical prefix matching). Use vault_list_property_keys to discover valid keys and vault_list_property_values to see what values a key takes.

Parameters:

  • key is exact and case-sensitive. Text values match exactly and case-sensitively, with no partial matching or globbing. Stored numbers also match numerically: "04" and "4.0" match number 4 and their own literal text, but not text "4".

  • Numeric matching accepts complete finite YAML core numeric forms: signed decimals, leading-zero decimals, .5, 4., exponents, 0x hexadecimal and 0o octal. Whitespace, final line breaks, prefixes like "4abc", comments, expressions, 0b binary, separators, non-finite values and overflow match only literal text.

  • Numeric equality uses stored number precision: large integers can round to the same value, and underflow such as "1e-999" matches stored zero.

  • Pass a checkbox as "1" or "0" (true is stored as 1, false as 0); "1.0" does not match a checked checkbox.

  • An array element must equal value in full: "blog" matches tags: ["blog", "draft"] but not tags: ["my-blog"].

  • folder names a whole folder and includes its subfolders: "Projects" covers "Projects/Archive" but not "ProjectsOld/". Matching ignores ASCII letter case; omit folder to search the entire vault.

  • limit applies after sorting. Nothing in the response signals truncation: exactly limit results may mean more exist, so raise limit to check.

Errors:

  • An unknown key or unmatched value returns an empty array, not an error.

Returns: JSON array of note metadata (path, title, tags, related, folder, type, created, modified, bytes, leading_callout?, additional_properties), sorted by filesystem mtime descending — recently-synced notes may sort ahead of older content edits.

  • leading_callout appears only when the note has a leading callout.

  • additional_properties appears only when frontmatter has keys outside title, tags, type, created, and related.

ParametersJSON Schema
NameRequiredDescriptionDefault
keyYesProperty key name (e.g. "status", "type", "tags"). Use vault_list_property_keys to discover valid keys.
limitNoMax results (default 20)
valueYesValue to match (e.g. "active", "4", "1e-7"). Use vault_list_property_values to discover valid values for a key.
folderNoRestrict to a folder (e.g. "Projects")

TDQS

A4.2/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover the safety profile (readOnly, idempotent, non-destructive), so the bar is lower. The description adds substantial non-obvious behavior: exact/case-sensitive matching, numeric coercion rules, checkbox '1'/'0' encoding, subfolder inclusion, silent truncation when limit is hit, and that an unknown key returns an empty array rather than an error. This is well above what annotations provide.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well front-loaded with purpose, examples, routing, then parameter detail. The numeric-matching paragraph is dense and covers edge cases beyond what most callers need, but the sectioning keeps it navigable.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, yet the description fully specifies the return shape (metadata fields, optional leading_callout/additional_properties, sort order) and the truncation caveat. Nothing an agent needs to call or interpret results correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so baseline is 3. The 'Parameters' block does add meaning beyond the schema (case-sensitivity, numeric/checkbox forms, folder covering subfolders, limit applied post-sort), but much of it restates or elaborates fields the schema already names.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Find notes where a frontmatter property matches a value') and immediately scopes it as metadata-only with no text query. It clarifies the scalar-vs-array matching behavior, which is the key behavioral distinction from sibling search tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit 'When to use' section, plus named alternatives with selection conditions: prefer vault_search with a text query, prefer vault_search_by_tag for hierarchical tag matching, and use vault_list_property_keys/values for discovery. Routing is unambiguous.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

vault_search_by_tagSearch by TagA
Read-onlyIdempotent

Find notes with a specific tag. By default uses hierarchical prefix matching — a parent tag matches all children (e.g. "project" matches "project/vault-cortex", "project/blog"). Set exact=true for exact match only.

Example: vault_search_by_tag({ tag: "project" })

When to use: Tag-only lookups, for one tag or a whole tag hierarchy, with no text query. Prefer vault_search when you also need text-based relevance ranking. Use vault_list_tags first to discover available tags.

Parameters:

  • tag + exact interact: the prefix match follows the "/" separator, so "project" matches itself and its children but does NOT match "my-project" or "projects". exact=true matches only the literal tag, excluding children.

Errors:

  • An unknown tag or no matches returns an empty array, not an error — don't use as an existence check.

Returns: JSON array of note metadata (path, title, tags, related, folder, type, created, modified, bytes, leading_callout?, additional_properties?), sorted by most recently modified and capped at 20, with no truncation signal: exactly 20 results may mean more exist. bytes is the on-disk file size. Promoted keys are in top-level fields; additional_properties contains only unpromoted keys.

ParametersJSON Schema
NameRequiredDescriptionDefault
tagYesTag name without "#" prefix (e.g. "project", "session-log"). Hierarchical tags use "/" separators (e.g. "project/vault-cortex").
exactNoExact match only (default: false, prefix match)

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover readOnly/idempotent/non-destructive, yet the description adds genuinely new behavior: an unknown tag or no match returns an empty array rather than an error and should not be used as an existence check, plus a result cap of 20 with no truncation signal so exactly 20 may mean more. That cap/truncation caveat is important and is not expressible in the annotations. Minor gap: no pagination/offset guidance for retrieving past the cap.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the one-line purpose, then labeled blocks (Example, When to use, Parameters, Errors, Returns) that make scanning cheap. The Returns block is dense but earns its length because there is no output schema; the Parameters block repeats a little of what the schema already states.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description supplies the full return contract (field list, promoted vs additional_properties keys, sort order, cap of 20), plus error semantics and the exact-vs-prefix interaction. Nothing an agent needs to call this correctly or interpret results is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, but the description still adds meaning beyond it: the prefix match follows the '/' separator, so 'project' matches its children but NOT 'my-project' or 'projects', and exact=true excludes children entirely. Those boundary/negative examples are the exact failure modes an agent would hit and are absent from the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a specific verb+resource ('Find notes with a specific tag') and immediately scopes the tool's distinct capability (hierarchical prefix matching on a tag, with an exact override). The 'When to use' block explicitly separates it from vault_search (text relevance) and vault_list_tags (tag discovery), so an agent can route without opening sibling schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

States the selection condition plainly ('Tag-only lookups ... with no text query'), names the alternative to prefer when text ranking is needed, and gives a prerequisite step ('Use vault_list_tags first to discover available tags'). Exclusions and alternatives are both explicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

vault_update_memoryUpdate MemoryA
Idempotent

Append a dated entry to a section of a memory file in About Me/. The server prefixes the date automatically ("- YYYY-MM-DD: entry text") and inserts newest-first by default. Idempotent — an exact duplicate (same date + text in the same section) is a no-op, so retrying a timed-out call is safe. Memory files are append-only by default: when a preference changes, append the new state (newest wins) rather than deleting the old one. A file may declare entry-policy: living in frontmatter (surfaced by vault_list_memory_files) — a current-state file where pruning expired entries is expected maintenance rather than a violation.

Example: vault_update_memory({ file: "Opinions", section: "Code patterns (newest first)", entry: "Prefer immutable data structures" })

When to use: Recording a new preference, principle, opinion, or fact about the user. Call vault_list_memory_files first and reuse existing file and section names so entries stay grouped. Prefer vault_write_note for creating non-memory notes. A missing file or section is created automatically (new sections get "(newest first)" appended; new files get a placeholder scope callout to fill in via vault_replace_in_note). A new section name nearly identical to an existing heading (an HTML-entity slip, typo, or spacing variation) is rejected, so a mistyped name cannot silently fragment the file — names differing only in digits (e.g. "2025" vs "2026") are distinct.

Parameters:

  • options.position — "top" (default, newest-first) inserts above existing entries; "bottom" appends below them.

Obsidian syntax: Entry text is Obsidian Flavored Markdown. Watch for: #word = tag, [[ = wikilink. Escape with # or backticks when unintentional.

Errors:

  • "refusing memory write: … would shrink content" — safety guard: the write would leave the file at under half its size, which happens when the file holds content a rewrite cannot keep (for example long YAML comments in its properties). A retry returns the same refusal until a manual edit removes that content; inspect the file with vault_read_note to find it.

  • "entry must be a single line" — memory entries are single dated bullets; collapse newlines or append multiple entries.

  • "section must be a single line" — section names become H2 headings; remove line breaks.

  • "date must be a real ISO calendar date" — options.date only accepts an existing calendar date in bare YYYY-MM-DD form (e.g. "2026-07-02"), not a timestamp.

  • "entry contains a control character" / "section contains a control character" — the value includes a non-printable control byte; remove it before writing.

  • "memory file must not start with a dot" / "memory file must be a bare name without path separators" — use a bare file name: no folder or slash, and no leading dot (that would create a hidden file, invisible in Obsidian and to every listing).

  • "section not created: … is nearly identical to existing section …" — near-duplicate guard; pass the exact existing heading (listed in the error) to append there, or choose a clearly different name for a genuinely new section.

Returns: Confirmation message (notes when an identical entry already existed and nothing was written).

ParametersJSON Schema
NameRequiredDescriptionDefault
fileYesMemory file name without .md (e.g. "Principles"). Use the exact letter case; a different case can create a second file.
entryYesRaw entry text — a single line (newlines are rejected); the server prepends "- **YYYY-MM-DD**: " automatically. Do not include the date or bullet prefix.
optionsNoOptional date and position overrides
sectionYesH2 section heading (e.g. "Decision heuristics (newest first)"). Matched case-insensitively, with or without the "(newest first)" suffix.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare idempotentHint/destructiveHint/readOnlyHint, but the description adds substantial behavioral context: automatic date prefixing, newest-first insertion, append-only policy with the entry-policy: living exception, auto-creation of missing files/sections, the near-duplicate heading guard, and the shrink-guard refusal behavior. This far exceeds what the annotations convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core action and behavior, then organized into example, when-to-use, parameters, syntax, errors, and returns. Every section earns its place, though the extensive error catalog makes it long; the density is justified by the tool's complexity but is near the upper bound of acceptable size.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Despite having no output schema, the description explicitly covers the return value ('Confirmation message... notes when an identical entry already existed'), plus edge cases (auto-creation, near-duplicate guard, shrink refusal). Nothing an agent needs to invoke this correctly or interpret failures is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, and the description modestly exceeds it by tying parameter constraints to concrete error conditions (single-line entry/section, bare YYYY-MM-DD date, bare file name with no dot or slash). It adds operational meaning beyond the schema field descriptions rather than merely restating them.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a precise verb and resource: 'Append a dated entry to a section of a memory file in About Me/.' It explicitly differentiates from siblings by naming vault_write_note for non-memory notes and vault_list_memory_files for discovery, so an agent can route correctly without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides an explicit 'When to use' clause (recording a preference, principle, opinion, or fact about the user) plus a prerequisite workflow ('Call vault_list_memory_files first and reuse existing file and section names'). It also names the alternative for a different case ('Prefer vault_write_note for creating non-memory notes').

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

vault_update_propertiesUpdate PropertiesA
DestructiveIdempotent

Update a note's frontmatter properties via shallow merge — new keys added, matching keys overwritten, null deletes a key, unmentioned keys preserved. Body is never modified.

Example: vault_update_properties({ path: "Projects/todo.md", properties: { status: "active", draft: null } })

When to use: Changing tags, status, type, or any property without reading/rewriting the full note body. Prefer vault_write_note when creating a new note, or replacing the body (with overwrite: true). Read current properties first with vault_read_note({ properties_only: true }) — arrays are replaced entirely, not appended to.

Errors:

  • "note not found" — path does not exist; create the note first with vault_write_note

  • "path must end in …" — add the .md extension

  • "absolute path blocked" / "path traversal blocked" / "hidden path blocked" — use a vault-relative path with no hidden (dot-prefixed) file or folder in it

  • "concurrent write in progress" — another write to this note is in flight; re-read the note and retry

Obsidian syntax: Use arrays for multi-value fields (tags: [a, b]), quote wikilinks ("[[Note]]"), keep types consistent (mismatches cause silent query failures).

Returns: Confirmation message.

ParametersJSON Schema
NameRequiredDescriptionDefault
pathYesVault-relative path to the note, including the ".md" extension. Use the exact letter case.
propertiesYesProperties to merge; a null value deletes that key.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare the write/destructive/idempotent profile, and the description adds substantial non-redundant behavior: shallow-merge semantics, null-deletes-a-key, unmentioned keys preserved, body untouched, and a full error catalog including concurrency retry guidance. The null-delete behavior is exactly the kind of detail annotations cannot express, and it is consistent with destructiveHint=true.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the merge contract, then example, when-to-use, errors, syntax notes, return value — a well-labeled structure where each block earns its place. It is on the longer side, and the Obsidian syntax paragraph is slightly tangential to tool selection, but nothing is padding.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, so the 'Returns: Confirmation message.' line covers the return contract, and the error list, merge semantics, and format caveats cover the rest of what an agent needs. For a 2-param nested-object mutation tool, this is complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, and the description genuinely adds meaning on top: a concrete call example, the rule that arrays are replaced entirely rather than appended, and Obsidian type/quoting conventions for wikilinks and multi-value fields. Only the 'exact letter case' nuance of the path param is left to the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Update a note's frontmatter properties via shallow merge') and immediately scopes the operation: body is never modified. This cleanly separates it from siblings like vault_write_note, vault_patch_note, and vault_replace_in_note.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit 'When to use' clause plus an explicit alternative ('Prefer vault_write_note when creating a new note, or replacing the body with overwrite: true') and a prerequisite step pointing at vault_read_note({ properties_only: true }). Both the when and the when-not are named.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

vault_update_taskUpdate TaskA
Destructive

Update a task's status, priority, description, dates, dependencies, block_id, checklist items, or heading placement in one call. Any combination of these can change together — every field passed is written in a single edit.

Example: vault_update_task({ path: "TASKS.md", block_id: "my-task", status: "done" }) — complete a task; on a Kanban board, auto-moves to the done lane; a recurring task (🔁) spawns its next occurrence Example: vault_update_task({ path: "TASKS.md", block_id: "my-task", recurrence: "every week" }) — make a task recurring (null removes the rule) Example: vault_update_task({ path: "TASKS.md", block_id: "my-task", heading: "Done" }) — move a task to a different heading (lands at the top of the lane by default) Example: vault_update_task({ path: "TASKS.md", block_id: "my-task", description: "Updated task name", due: "2026-10-01", scheduled: null }) — change the description, set one date, and clear another Example: vault_update_task({ path: "TASKS.md", block_id: "my-task", status: "in_progress", add_subtasks: ["Design", "Implement", "Test"] }) — start working and add checklist stages Example: vault_update_task({ path: "TASKS.md", line: 42, assign_block_id: "my-task" }) — add a block_id to a task that lacks one Example: vault_update_task({ path: "TASKS.md", block_id: "my-task", heading: "Active", position: 3 }) — move to the 3rd position in a lane

When to use: Any change to an existing task — completing, starting, re-prioritizing, editing text, setting or clearing dates, adding checklist items, assigning block_ids, moving between headings, or reordering within a lane. Use vault_list_tasks first to get identification fields (path + block_id or line). For creating a new task, use vault_create_task instead.

Parameters:

  • Exactly one of block_id or line is required to identify the task.

  • At least one change is required. Clearing is always explicit null — omitting a field leaves it untouched.

  • status manages the checkbox and the done/cancelled dates. Kanban: "done" moves the card and its checklist sub-items to the done lane (sub-item checkboxes left as they are); a sub-task stays under its parent. Recurring (🔁): spawns the next occurrence above the completed one (below with the plugin's "next line" setting), dates advanced per the rule. The spawn stays in the source lane with no block_id, 🆔, or ⛔ — follow up with assign_block_id on next_occurrence.line. Completing by line is NOT idempotent for recurring tasks (the spawn occupies the old line); prefer block_id. Delete (🏁 delete / [onCompletion:: delete]): removes the task line and children instead of moving to done. With 🔁 + 🏁, the spawn is created first, then the completed line is removed; with "next line", children transfer to the spawn. Result carries on_completion_applied: "delete".

  • recurrence: a rule ending "when done" bases the next occurrence on the completion day. When set together with status "done", the new rule governs the spawn. Setting recurrence to null while completing removes the rule and completes without spawning.

  • on_completion: passed together with status, the submitted value governs the delete decision — "keep" while completing a "delete" task prevents the deletion.

  • position: applies to a heading move or an auto-done-lane move. Without a heading, it triggers a same-lane reorder to the given position; omitting position performs no reorder. Ignored when the task is deleted on completion. Not valid on sub-tasks.

Errors:

  • "note not found" — path does not exist

  • "path must end in …" — add the .md extension

  • "absolute path blocked" / "path traversal blocked" / "hidden path blocked" — use a vault-relative path with no hidden (dot-prefixed) file or folder in it

  • "exactly one of blockId or line is required" / "blockId and line are mutually exclusive" — pass exactly one of block_id or line

  • "blockId ... not found" — no task line in the note ends with ^block_id

  • "blockId ... is inside a fenced code block or comment" — the block_id matches a line inside a fenced code block or %% %% comment; target a line outside the fence

  • "no task at line N" — line doesn't contain a task checkbox

  • "line N is inside a fenced code block or comment" — the line is inside a fenced code block or %% %% comment; target a line outside the fence

  • "checkbox "[c]" is a NON_TASK status" — the task's checkbox char is typed NON_TASK in the Tasks plugin's status registry, so it is not a task; to change that, retype it there and restart the server

  • "no checkbox symbol for status ..." — the status registry has no symbol for the target status and the built-in default is retyped; update the plugin's status registry to include a symbol for this status, then restart the server

  • "at least one mutation" — no change params provided

  • "cannot move a sub-task to a heading" — explicit heading on a task nested under another task (depth > 0 in vault_list_tasks)

  • "cannot reposition a sub-task" — explicit position on a sub-task (sub-tasks move with their parent)

  • "cannot reorder a task that sits above the first heading" — position without a heading on a task before the first section heading

  • "cannot reorder within "X" — the heading appears N times" — same-lane reorder on a card whose heading name is duplicated in the note; rename one section to make it unique

  • "cannot place at position N under "X" — the heading appears N times" — cross-lane move with an integer position to a heading name that appears more than once; rename one section to make it unique

  • "heading "X" not found; available: ..." — target heading doesn't exist; the error lists the note's headings

  • "multiple done lanes detected" — status "done" on a Kanban board with more than one Complete-marked lane; pass heading to pick the lane

  • "no done lane detected" — status "done" on a Kanban board with no Complete marker and no "Done" heading; pass heading explicitly

  • "blockId ... already exists" / "blockId ... contains invalid characters" — assign_block_id must be unique in the note and match [a-zA-Z0-9-]+

  • "invalid date" — a date param fails calendar validation

  • "description cannot be empty" / "addSubtasks cannot contain an empty item" — whitespace-only description or checklist item

  • "description must be a single line" / "addSubtasks items must be a single line" — a task is one file line; a line break in the text would split its metadata onto a line the parser never reads

  • "taskId ... contains invalid characters" / "dependsOn entry ... contains invalid characters" — task_id and every depends_on entry must match [a-zA-Z0-9_-]+ (the Tasks plugin's id grammar)

  • "unrecognized recurrence rule ..." — the rule text is not Tasks-plugin natural language; written as-is it would silently never recur

  • "concurrent write in progress" — another write to this note is in flight; retry

Obsidian syntax: The Tasks plugin reads metadata off the END of a task line. A trailing signifier in description or add_subtasks text (an emoji field like "🔁 every week", or a Dataview [key:: value] field) that the plugin's parser recognizes as a field — followed only by other recognized fields — is read back as metadata, not text. Whether it is captured depends on the field's value grammar: 🔁 reads any trailing words as its recurrence rule, while 📅 followed by non-date words stays description text. The same interference can change the value an adjacent field reads back with, or make a field appear that was never set. The write still succeeds either way; when the stored line would read back differently than this call set, the result carries an advisories array naming each divergence. The dates a status change stamps or clears (the ✅/❌ dates) produce no advisories on their own — but a description signifier that changes what the stamped date parses back as is still reported.

Returns: JSON { path, line, description, block_id, heading, subtasks, next_occurrence, changes, advisories, on_completion_applied } — line is the final 1-based position (when on_completion_applied is "delete", it is the position the task occupied before removal); description is the current text; block_id and heading reflect the task after the update (block_id is omitted when the task has none, heading when the task sits above the first heading); subtasks lists each checklist item added by add_subtasks as { line, description } (omitted when none were added) — checklist items carry no block_id, so line is the handle for a follow-up update; next_occurrence is present only when a completion spawned a recurring task's next occurrence: { line, description, due?, scheduled?, start? } with only the dates the occurrence has — it carries no block_id, so line is its handle; changes lists every field applied as "field: before → after", with "(none)" for an absent value (for subtasks the two sides are checklist-item counts, and a spawn adds "next_occurrence: (none) → line N"); advisories (omitted when there are none) lists one sentence for each: a stored line that parses back differently than submitted (see Obsidian syntax above), a duplicate tag removed from the line, or a completed recurring task whose rule yields no next occurrence (unreadable rule text or a rule with no dates left); on_completion_applied (present only when the effective on_completion was delete — pre-existing on the task or set in the same call — and it was transitioned to done) is always "delete".

ParametersJSON Schema
NameRequiredDescriptionDefault
dueNoDue date (YYYY-MM-DD) to set, or null to clear.
lineNo1-based line number from vault_list_tasks. Fragile if the file changed since the query.
pathYesVault-relative path to the note containing the task (must end in ".md"). Use the exact letter case.
startNoStart date (YYYY-MM-DD) to set, or null to clear.
formatNoField format for new metadata. Default: auto-detected from .obsidian/ config, falling back to emoji.
statusNoTarget status. "done" appends the ✅ date; "cancelled" appends the ❌ date.
createdNoCreated date (YYYY-MM-DD) to set or clear. Typically auto-stamped; use for corrections.
headingNoTarget heading to move the task to. On Kanban boards this is a lane move; works on any note with headings. Not valid on sub-tasks.
task_idNoTasks plugin 🆔 identifier to set, or null to clear.
block_idNoStable task identifier — the ^block-id at the end of the task line, without the ^. Preferred over line.
positionNoWhere within the target heading the task lands. "top" or "bottom" for the extremes; an integer (1-based) for an exact position among the lane's top-level cards (sub-tasks move with their parent and are not counted). Position 1 is the first card. A position past the card count lands directly below the last card (unlike "bottom", which appends after any non-task text at the end of the section). Defaults to "top" on heading moves.
priorityNoPriority signifier to set, or null to remove it.
scheduledNoScheduled date (YYYY-MM-DD) to set, or null to clear.
depends_onNoTasks plugin ⛔ dependency IDs to set (non-empty), or null to clear.
recurrenceNoTasks plugin 🔁 rule in natural language (e.g. "every week", "every month on the 15th", "every 2 weeks when done") to set, or null to remove it. Completing the task spawns its next occurrence.
descriptionNoNew task description text. Replaces the existing description; metadata fields and block_id are preserved.
add_subtasksNoChecklist items to append, one indented todo line each, under the task's existing items — never replaces them. Can be combined with any other change; not appended when the same call removes the task (on_completion delete). For full sub-tasks with metadata, use vault_create_task with parent_block_id.
on_completionNoTasks plugin 🏁 onCompletion action to set, or null to remove it. "delete" removes the task line on completion; "keep" leaves it in place (which is also the behavior when no 🏁 field exists on the task). Omitting this parameter leaves the field unchanged.
assign_block_idNoAdd or replace the ^block-id on the task line. Letters, digits, and hyphens only; must be unique within the note.

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations flag destructiveHint=true and idempotentHint=false, and the description substantiates both with concrete detail: Kanban lane moves, the delete-on-completion path that removes the line and children, the recurring spawn's loss of block_id/🆔/⛔, and the explicit caveat that completing by line is NOT idempotent for recurring tasks. It goes well beyond the annotation surface, covering concurrency ('concurrent write in progress'), auth/path blocking, and the advisories divergence mechanism.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The structure is strong — purpose, examples, usage, parameters, errors, syntax notes, and returns are clearly headed and front-loaded. However, the description is enormous relative to the task, and the exhaustive error catalog (24 entries) plus seven examples is heavier than an agent needs to select and invoke correctly, so it is complete rather than concise.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the 'Returns' section carries the full burden and documents every returned field including next_occurrence, changes, advisories, and on_completion_applied. Combined with the mutation/error/recurrence semantics, the definition is complete for a 19-parameter mutating tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3; the description earns above that by documenting cross-parameter semantics the schema cannot express: exactly-one-of block_id/line, mutual exclusivity, the explicit-null clearing convention, position's interaction with heading moves (and its invalidity on sub-tasks), and how on_completion/recurrence govern the status='done' transition. Some field-level restatement overlaps the schema, keeping this from a 5.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb+resource ('Update a task's status, priority, description, dates...') and enumerates the full set of mutable fields. It explicitly distinguishes itself from siblings: vault_list_tasks for identification and vault_create_task for creation. An agent can place this tool precisely without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The 'When to use' paragraph gives explicit conditions (completing, starting, re-prioritizing, editing, setting/clearing dates, moving headings, reordering) and names the alternatives to use first or instead (vault_list_tasks, vault_create_task). It also states the preconditions for a valid call — exactly one of block_id/line, at least one change, explicit null for clearing.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

vault_write_noteWrite NoteA
Destructive

Create a markdown note. Errors if a note already exists at the path unless overwrite is set. Body replaces the entire note content: existing content will be lost unless you include it in body, so do not use this tool for surgical edits to large files. Properties are passed separately and merged with any existing properties when overwriting (new keys added, matching keys overwritten, keys set to null removed, unmentioned keys preserved); overwriting without properties keeps the existing property values.

Example: vault_write_note({ path: "Projects/notes.md", body: "# Notes\n\nProject notes here.", properties: { tags: ["project"], type: "project" } }) Example: vault_write_note({ path: "Projects/notes.md", body: "Updated content.", overwrite: true })

When to use: Creating a new note. Set overwrite: true only when you intend to replace an existing note's body. Prefer vault_update_properties for property-only edits (no body round-trip). Prefer vault_update_memory for appending dated entries to About Me/ memory files.

Errors:

  • "note already exists" — a note already lives at this path; set overwrite: true to replace it, or use vault_patch_note / vault_replace_in_note for partial edits

  • "path must end in …" — add the .md extension

  • "cannot write note …: that path is not a file" — a folder already has this name; choose another path

  • "absolute path blocked" / "path traversal blocked" / "hidden path blocked" — use a vault-relative path with no hidden (dot-prefixed) file or folder in it

  • "concurrent write in progress" — another write to this note is in flight; re-read the note and retry

  • "body contains a control character" — body includes a non-printable control byte; remove it before writing

Obsidian syntax: Body is Obsidian Flavored Markdown (no escaping applied). Watch for: #word = tag (escape with #), [[ = wikilink, %% = comment block. In properties: quote wikilink values ("[[Note]]"), use YAML lists for tags, keep property types consistent (string/number/list mismatches cause silent query failures).

Returns: Confirmation message.

ParametersJSON Schema
NameRequiredDescriptionDefault
bodyYesMarkdown body content — do not include frontmatter fences (---); use the properties parameter instead.
pathYesVault-relative path including the ".md" extension (e.g. "Projects/notes.md"). Parent folders are created as needed. Use the exact letter case; a different case can create a duplicate note or folder.
overwriteNoAllow overwriting an existing note (default: false — errors if file exists).
propertiesNoOptional properties to merge; a null value deletes that key.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare destructiveHint=true and idempotentHint=false, but the description goes well past them: it explains exactly what is destroyed (body replaces the entire note content), how properties merge on overwrite (new keys added, matching overwritten, null removes, unmentioned preserved), concurrency behavior, and an enumerated error catalogue including path-traversal and control-character failures. This is materially richer than the annotations alone.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads purpose and the irreversible body-replacement warning before examples and error codes; each block (when-to-use, errors, syntax) earns its place. It is longer than strictly necessary — the error catalogue is exhaustive — but the information density remains high and scannable.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a destructive, non-idempotent, 4-parameter write tool with a nested properties object and no output schema, the description covers data-loss risk, overwrite semantics, sibling routing, path rules, syntax pitfalls, and failure modes. Nothing an agent needs to call this safely is missing; the 'Returns: Confirmation message.' line closes the output gap.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3, but the description adds real meaning beyond it: the merge semantics for properties (what null does to a key, what happens to unmentioned keys when overwriting without properties) and the overwrite gating rule. It also flags Obsidian-specific quirks (#, [[, %%) that affect body and property values, which the schema does not cover.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Create a markdown note') and immediately distinguishes itself from sibling write tools by contrast: vault_patch_note / vault_replace_in_note for partial edits and vault_update_properties for property-only edits. An agent can route correctly without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Contains an explicit 'When to use' section, a conditional rule for overwrite ('only when you intend to replace an existing note's body'), and names two preferred alternatives with the conditions that select them (property-only edits, appending dated memory entries). No exclusions are left implicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 18 tool updatesv0.54.9
    • Changedvault_create_task5 fields changed
      • changedInput schema / properties / heading / description
        Previous value: -"Target heading. Required on Kanban boards; optional on regular notes (omit to append at end of body)."New value: +"Target heading. On a regular note, omit to append at end of body."
      • changedInput schema / properties / parent_block_id / description
        Previous value: -"^block-id (without the ^) of an existing task to nest under as a sub-task. Mutually exclusive with parent_line and heading."New value: +"^block-id (without the ^) of an existing task to nest under as a sub-task."
      • changedInput schema / properties / parent_line / description
        Previous value: -"1-based line number of an existing task to nest under as a sub-task. Mutually exclusive with parent_block_id and heading. Fragile if the file changed since the line was read."New value: +"1-based line number of an existing task to nest under as a sub-task. Fragile if the file changed since the line was read."
      • changedInput schema / properties / position / description
        Previous value: -"Where within the heading section the task is placed. \"top\" or \"bottom\" for the extremes; an integer (1-based) for an exact position among the lane's top-level cards (sub-tasks move with their parent and are not counted). Position 1 is the first card. A position past the card count lands directly below the last card (unlike \"bottom\", which appends after any trailing section content). Defaults to bottom. Kanban boards with new-card-insertion-method set to prepend default to top instead. Ignored when no heading or when placing under a parent."New value: +"Where within the heading section the task is placed. \"top\" or \"bottom\" for the extremes; an integer (1-based) for an exact position among the lane's top-level cards (sub-tasks move with their parent and are not counted). Position 1 is the first card. A position past the card count lands directly below the last card (unlike \"bottom\", which appends after any non-task text at the end of the section). Defaults to bottom."
      • changedInput schema / properties / recurrence / description
        Previous value: -"Tasks plugin 🔁 rule in natural language (e.g. \"every week\", \"every 2 weeks when done\"). Completing the task spawns its next occurrence."New value: +"Tasks plugin 🔁 rule in natural language (e.g. \"every week\", \"every month on the 15th\", \"every 2 weeks when done\"). Completing the task spawns its next occurrence."
    • Changedvault_delete_memory1 field changed
      • changedInput schema / properties / entry / description
        Previous value: -"Exact entry text as shown by vault_get_memory — without the \"- **YYYY-MM-DD**: \" prefix or bullet. Both date and entry must match for deletion."New value: +"Exact entry text as shown by vault_get_memory — without the \"- **YYYY-MM-DD**: \" prefix or bullet."
    • Changedvault_delete_span1 field changed
      • changedInput schema / properties / end_anchor / description
        Previous value: -"Short, unique substring that identifies the LAST line of the block, searched at or after the start_anchor line. The entire line is selected. Omit to delete just the single line containing start_anchor."New value: +"Short substring that identifies the LAST line of the block. The entire line is selected. Omit to delete just the single line containing start_anchor."
    • Changedvault_find_orphans1 field changed
      • changedInput schema / properties / exclude_folders / description
        Previous value: -"Folders to exclude — replaces the defaults ([\"Daily Notes\",\"Templates\",\"About Me\"]), not merged"New value: +"Folder paths to exclude (e.g. Projects; default: daily notes folder, Templates, \"About Me\")"
    • Changedvault_insert_at_anchor1 field changed
      • changedInput schema / properties / content / description
        Previous value: -"Content to insert (one or more lines), inserted verbatim as whole lines — blank lines are kept, and a trailing newline adds a blank line after the block."New value: +"Content to insert (one or more lines)."
    • Changedvault_list_files2 fields changed
      • addedInput schema / properties / extensions / minItems
        Added value: +1
      • changedInput schema / properties / folder / description
        Previous value: -"Folder path to search recursively (e.g. \"attachments\"). Omit to list the whole vault."New value: +"Folder path to search recursively (e.g. \"attachments\" or \"Projects/media\"). Omit to list the whole vault."
    • Changedvault_list_tasks3 fields changed
      • addedInput schema / properties / priority / minItems
        Added value: +1
      • changedInput schema / properties / sort_by / description
        Previous value: -"Sort key (default \"due\"). Date sorts cascade through related fields when the primary is absent; each fallback uses its own natural direction. \"position\" sorts by file path then line number — the natural order for Kanban boards."New value: +"Sort key (default \"due\"). Date sorts cascade through related fields when the primary is absent (through the rest of due → scheduled → start → created, in that order; done does not cascade); each fallback uses its own natural direction. \"position\" sorts by file path then line number — the natural order for Kanban boards."
      • changedInput schema / properties / status / description
        Previous value: -"Status filter, OR-combined (default \"not_done\" = todo + in_progress, excluding done and cancelled). Virtual values expand in arrays: \"not_done\" adds todo + in_progress, \"all\" includes every status."New value: +"Status filter, OR-combined (default \"not_done\" = todo + in_progress, excluding done and cancelled). \"all\" includes every status."
    • Changedvault_memory_recall1 field changed
      • changedInput schema / properties / limit / description
        Previous value: -"Cap on returned entries (default 50). When more match, the least-relevant are dropped and truncated=true — never a date range."New value: +"Cap on returned entries (default 50)."
    • Changedvault_patch_note3 fields changed
      • changedInput schema / properties / content / description
        Previous value: -"Markdown content to insert, written verbatim with no separator added — end it with a newline to leave a blank line after the inserted block. Must not begin with the target heading text (it would duplicate the heading, which is kept automatically)."New value: +"Markdown content to insert. Must not begin with the target heading text (it would duplicate the heading, which is kept automatically)."
      • changedInput schema / properties / heading / description
        Previous value: -"Target heading text (case-sensitive exact match). Required for replace and insert_before. Optional for append/prepend (omit for file-level operation)."New value: +"Target heading text (case-sensitive exact match). Omit for a file-level append or prepend."
      • changedInput schema / properties / operation / description
        Previous value: -"append | prepend | replace | insert_before. replace and insert_before require a heading; append and prepend work with or without one."New value: +"append | prepend | replace | insert_before."
    • Changedvault_read_note3 fields changed
      • changedInput schema / properties / heading / description
        Previous value: -"Return only this section (heading line + body, through the next same-or-higher heading). Case-sensitive exact match."New value: +"Return only this section. Case-sensitive exact match."
      • changedInput schema / properties / limit / description
        Previous value: -"Maximum lines returned (default: all remaining). A paged read's metadata line states the window, the total line count, and the next start_line."New value: +"Maximum lines returned (default: all remaining)."
      • changedInput schema / properties / start_line / description
        Previous value: -"First line to return, 1-based (default 1). Pages the delivered rendition (full body or a heading section). Not valid for outline or properties_only (JSON modes)."New value: +"First line to return, 1-based (default 1). Pages the text output (full body or a heading section). Not valid for outline or properties_only (JSON modes)."
    • Changedvault_replace_span2 fields changed
      • changedInput schema / properties / content / description
        Previous value: -"Replacement content (one or more lines) — replaces every line of the matched span. Must be non-empty; use vault_delete_span to delete without replacement."New value: +"Replacement content (one or more lines) — replaces every line of the matched span. Must be non-empty."
      • changedInput schema / properties / end_anchor / description
        Previous value: -"Short, unique substring that identifies the LAST line of the block, searched at or after the start_anchor line. The entire line is selected. Omit to replace just the single line containing start_anchor."New value: +"Short substring that identifies the LAST line of the block. The entire line is selected. Omit to replace just the single line containing start_anchor."
    • Changedvault_search5 fields changed
      • changedInput schema / properties / filters / description
        Previous value: -"Optional structured filters — all conditions AND-combine with each other and with the text query"New value: +"Optional structured filters"
      • changedInput schema / properties / filters / properties / created / description
        Previous value: -"Created date bounds (YYYY-MM-DD) on the frontmatter \"created\" property — notes without a parseable value never match"New value: +"Created date bounds (YYYY-MM-DD) on the frontmatter \"created\" property"
      • changedInput schema / properties / include_leading_callout / description
        Previous value: -"If true, each result includes its leading_callout ({ type, title, body }) when present. Off by default to keep results lean."New value: +"If true, include each result's leading callout. Off by default to keep results lean."
      • changedInput schema / properties / query / description
        Previous value: -"Search query text — unquoted terms use implicit AND with stemming; wrap in double quotes for exact phrases"New value: +"Search query text"
      • changedInput schema / properties / snippet_tokens / description
        Previous value: -"Snippet length in tokens (default 30)"New value: +"Snippet length in words (default 30)"
    • Changedvault_search_by_folder1 field changed
      • changedInput schema / properties / recursive / description
        Previous value: -"Include subfolders (default: true)"New value: +"Include subfolders (default: true); false lists only the folder's top level"
    • Changedvault_search_by_property1 field changed
      • changedInput schema / properties / value / description
        Previous value: -"Value to match (exact, case-sensitive, e.g. \"active\", \"session-log\"). Use vault_list_property_values to discover valid values for a key."New value: +"Value to match (e.g. \"active\", \"4\", \"1e-7\"). Use vault_list_property_values to discover valid values for a key."
    • Changedvault_update_memory1 field changed
      • changedInput schema / properties / options / properties / date / description
        Previous value: -"ISO YYYY-MM-DD date (defaults to today)"New value: +"ISO YYYY-MM-DD date (defaults to today, server timezone)"
    • Changedvault_update_properties1 field changed
      • changedInput schema / properties / properties / description
        Previous value: -"Properties to merge. New keys are added; existing keys are overwritten; a null value deletes that key; unmentioned keys are preserved."New value: +"Properties to merge; a null value deletes that key."
    • Changedvault_update_task3 fields changed
      • changedInput schema / properties / position / description
        Previous value: -"Where within the target heading the task lands after a heading move or auto-done-lane move. \"top\" or \"bottom\" for the extremes; an integer (1-based) for an exact position among the lane's top-level cards (sub-tasks move with their parent and are not counted). Position 1 is the first card. A position past the card count lands directly below the last card (unlike \"bottom\", which appends after any trailing section content). Defaults to \"top\" on heading moves. Without a heading, triggers a same-lane reorder to the given position; omitting position entirely performs no reorder. Ignored when the task is deleted on completion. Not valid on sub-tasks."New value: +"Where within the target heading the task lands. \"top\" or \"bottom\" for the extremes; an integer (1-based) for an exact position among the lane's top-level cards (sub-tasks move with their parent and are not counted). Position 1 is the first card. A position past the card count lands directly below the last card (unlike \"bottom\", which appends after any non-task text at the end of the section). Defaults to \"top\" on heading moves."
      • changedInput schema / properties / recurrence / description
        Previous value: -"Tasks plugin 🔁 rule in natural language (e.g. \"every week\", \"every 2 weeks when done\") to set, or null to remove it. Completing the task spawns its next occurrence."New value: +"Tasks plugin 🔁 rule in natural language (e.g. \"every week\", \"every month on the 15th\", \"every 2 weeks when done\") to set, or null to remove it. Completing the task spawns its next occurrence."
      • changedInput schema / properties / status / description
        Previous value: -"Target status. \"done\" appends the ✅ date and, on a Kanban board, moves the card and its checklist sub-items to the done lane (sub-item checkboxes are left as they are); a task with 🏁 delete / [onCompletion:: delete] is removed from the file instead. \"cancelled\" appends the ❌ date."New value: +"Target status. \"done\" appends the ✅ date; \"cancelled\" appends the ❌ date."
    • Changedvault_write_note1 field changed
      • changedInput schema / properties / properties / description
        Previous value: -"Optional properties to merge. New keys are added; existing keys with matching names are overwritten; a null value deletes that key; unmentioned keys are preserved from the existing file."New value: +"Optional properties to merge; a null value deletes that key."
  2. 23 tool updatesv0.54.7
    • Changedvault_create_task1 field changed
      • changedInput schema / properties / path / description
        Previous value: -"Vault-relative path to the note (must end in \".md\"). The note must already exist."New value: +"Vault-relative path to the note (must end in \".md\"). The note must already exist. Use the exact letter case."
    • Changedvault_delete_memory2 fields changed
      • addedInput schema / properties / entry / minLength
        Added value: +1
      • changedInput schema / properties / file / description
        Previous value: -"Memory file name without .md (e.g. \"Principles\")"New value: +"Memory file name without .md (e.g. \"Principles\"). Use the exact letter case."
    • Changedvault_delete_note2 fields changed
      • changedInput schema / properties / path / description
        Previous value: -"Vault-relative path of the note to delete, including the \".md\" extension"New value: +"Vault-relative path of the note to delete, including the \".md\" extension. Use the exact letter case."
      • changedInput schema / properties / prune_empty_folders / description
        Previous value: -"When true, remove the note's parent folder(s) if deleting it leaves them empty, walking up to (but never including) the vault root. Default false matches Obsidian, which leaves empty folders in place. Only removes a folder with zero entries — a folder still holding any file, including a hidden one like .DS_Store, is left alone."New value: +"When true, also remove parent folders the delete leaves empty. Default false."
    • Changedvault_delete_span1 field changed
      • changedInput schema / properties / path / description
        Previous value: -"Vault-relative path to the note, including the \".md\" extension (e.g. \"Tracker.md\", \"Notes/Plan.md\")"New value: +"Vault-relative path to the note, including the \".md\" extension (e.g. \"Tracker.md\", \"Notes/Plan.md\"). Use the exact letter case."
    • Changedvault_get_daily_note1 field changed
      • addedInput schema / properties / date / minLength
        Added value: +1
    • Changedvault_get_memory1 field changed
      • changedInput schema / properties / file / description
        Previous value: -"Memory file name without .md (e.g. \"Principles\", \"Opinions\")"New value: +"Memory file name without .md (e.g. \"Principles\", \"Opinions\"). Use the exact letter case."
    • Changedvault_insert_at_anchor1 field changed
      • changedInput schema / properties / path / description
        Previous value: -"Vault-relative path to the note, including the \".md\" extension (e.g. \"Notes/Plan.md\", \"Tracker.md\")"New value: +"Vault-relative path to the note, including the \".md\" extension (e.g. \"Notes/Plan.md\", \"Tracker.md\"). Use the exact letter case."
    • Changedvault_list_notes4 fields changed
      • changedInput schema / properties / folder / description
        Previous value: -"Folder path prefix (e.g. \"About Me\", \"Projects\"). Includes all subfolders."New value: +"Vault-relative folder to list (e.g. \"About Me\", \"Projects\")."
      • addedInput schema / properties / folder / minLength
        Added value: +1
      • changedInput schema / properties / glob / description
        Previous value: -"Glob pattern for path filtering (e.g. \"**/*session-log*.md\"). Supports * and ** wildcards. Combined with folder when both are set."New value: +"Glob pattern for note paths (e.g. \"**/*session-log*.md\")."
      • addedInput schema / properties / glob / minLength
        Added value: +1
    • Changedvault_list_property_values2 fields changed
      • changedInput schema / properties / folder / description
        Previous value: -"Restrict to a folder prefix (e.g. \"Projects\")"New value: +"Restrict to a folder (e.g. \"Projects\")"
      • changedInput schema / properties / limit / description
        Previous value: -"Max values to return (default 50). Increase for high-cardinality properties."New value: +"Max values to return (default 50)."
    • Changedvault_list_tasks2 fields changed
      • changedInput schema / properties / folder / description
        Previous value: -"Restrict to a note-path prefix (e.g. \"Code Projects/vault-cortex\")"New value: +"Restrict to a folder (e.g. \"Code Projects/vault-cortex\")"
      • changedInput schema / properties / path / description
        Previous value: -"Restrict to one note (vault-relative path ending \".md\")"New value: +"Restrict to one note (vault-relative path ending \".md\", case-sensitive)"
    • Changedvault_memory_recall1 field changed
      • changedInput schema / properties / file / description
        Previous value: -"Optional: restrict to one memory file, name without .md (e.g. \"Opinions\"). Omit for cross-file recall — the default and usual choice."New value: +"Optional: restrict to one memory file, name without .md (e.g. \"Opinions\"), in its exact letter case. Omit for cross-file recall — the default and usual choice."
    • Changedvault_move_note3 fields changed
      • changedInput schema / properties / new_path / description
        Previous value: -"Destination vault-relative path (e.g. \"Projects/Spec.md\"). Must end in .md and must not already exist; parent folders are created as needed."New value: +"Destination vault-relative path (e.g. \"Projects/Spec.md\"). Must end in .md and must not already exist; parent folders are created as needed. Use the exact letter case of existing folders; a different case can create a second folder."
      • changedInput schema / properties / old_path / description
        Previous value: -"Current vault-relative path of the note to move (e.g. \"Inbox/Draft.md\"). Must end in .md."New value: +"Current vault-relative path of the note to move (e.g. \"Inbox/Draft.md\"). Must end in .md. Use the exact letter case."
      • changedInput schema / properties / prune_empty_folders / description
        Previous value: -"When true, remove the source folder(s) if the move leaves them empty, walking up to (but never including) the vault root. Default false matches Obsidian, which leaves empty folders in place. Only removes a folder with zero entries — an in-place rename or a move into a subfolder of the source leaves it non-empty and prunes nothing."New value: +"When true, also remove parent folders of old_path that the move leaves empty. Default false."
    • Changedvault_patch_note1 field changed
      • changedInput schema / properties / path / description
        Previous value: -"Vault-relative path to the note, including the \".md\" extension (e.g. \"TASKS.md\", \"Projects/plan.md\")"New value: +"Vault-relative path to the note, including the \".md\" extension (e.g. \"TASKS.md\", \"Projects/plan.md\"). Use the exact letter case."
    • Changedvault_read_file4 fields changed
      • changedInput schema / properties / limit / description
        Previous value: -"Maximum lines returned (default: all remaining). A paged read's metadata line states the window, the total line count, and the next start_line. The output byte cap still applies to the window — reduce limit if it overflows."New value: +"Maximum lines returned (default: all remaining)."
      • changedInput schema / properties / path / description
        Previous value: -"Vault-relative path to the file, including its extension (e.g. \"attachments/photo.png\", \"Boards/Roadmap.canvas\"). Must NOT end in \".md\" — notes are read with vault_read_note."New value: +"Vault-relative path to the file, including its extension (e.g. \"attachments/photo.png\", \"Boards/Roadmap.canvas\"). Must NOT end in \".md\" — notes are read with vault_read_note. Use the exact letter case."
      • changedInput schema / properties / raw / description
        Previous value: -"Return an alternative representation of the file. For .canvas this is the JSON Canvas source (geometry, ids, colors); for .pdf this renders pages as images instead of extracting text — useful for scanned documents, diagrams, and layout-sensitive content. Text formats already return their source, so raw changes nothing there. Images have no text source — raw returns an error."New value: +"Return the file's alternative form: JSON source for .canvas, page images for .pdf. Changes nothing for text formats, which already return their source. Rejected for images."
      • changedInput schema / properties / start_line / description
        Previous value: -"First line to return, 1-based (default 1). Pages any text result — text formats, canvas outlines and raw JSON, PDF-extracted text. Not valid for images or for PDFs with raw: true."New value: +"First line to return, 1-based (default 1)."
    • Changedvault_read_note1 field changed
      • changedInput schema / properties / path / description
        Previous value: -"Vault-relative path to the note, including the \".md\" extension (e.g. \"About Me/Principles.md\")"New value: +"Vault-relative path to the note, including the \".md\" extension (e.g. \"About Me/Principles.md\"). Use the exact letter case."
    • Changedvault_replace_in_note1 field changed
      • changedInput schema / properties / path / description
        Previous value: -"Vault-relative path to the note, including the \".md\" extension (e.g. \"Projects/plan.md\")"New value: +"Vault-relative path to the note, including the \".md\" extension (e.g. \"Projects/plan.md\"). Use the exact letter case."
    • Changedvault_replace_span1 field changed
      • changedInput schema / properties / path / description
        Previous value: -"Vault-relative path to the note, including the \".md\" extension (e.g. \"Tracker.md\", \"Notes/Plan.md\")"New value: +"Vault-relative path to the note, including the \".md\" extension (e.g. \"Tracker.md\", \"Notes/Plan.md\"). Use the exact letter case."
    • Changedvault_search1 field changed
      • changedInput schema / properties / filters / properties / folder / description
        Previous value: -"Restrict to a folder path prefix (e.g. \"Projects\")"New value: +"Restrict to a folder (e.g. \"Projects\")"
    • Changedvault_search_by_property2 fields changed
      • changedInput schema / properties / folder / description
        Previous value: -"Restrict to a folder prefix (e.g. \"Projects\")"New value: +"Restrict to a folder (e.g. \"Projects\")"
      • changedInput schema / properties / limit / description
        Previous value: -"Max results (default 20). Increase for broad metadata queries."New value: +"Max results (default 20)"
    • Changedvault_update_memory1 field changed
      • changedInput schema / properties / file / description
        Previous value: -"Memory file name without .md (e.g. \"Principles\")"New value: +"Memory file name without .md (e.g. \"Principles\"). Use the exact letter case; a different case can create a second file."
    • Changedvault_update_properties1 field changed
      • changedInput schema / properties / path / description
        Previous value: -"Vault-relative path to the note, including the \".md\" extension"New value: +"Vault-relative path to the note, including the \".md\" extension. Use the exact letter case."
    • Changedvault_update_task1 field changed
      • changedInput schema / properties / path / description
        Previous value: -"Vault-relative path to the note containing the task (must end in \".md\")"New value: +"Vault-relative path to the note containing the task (must end in \".md\"). Use the exact letter case."
    • Changedvault_write_note1 field changed
      • changedInput schema / properties / path / description
        Previous value: -"Vault-relative path including the \".md\" extension (e.g. \"Projects/notes.md\"). Parent folders are created as needed."New value: +"Vault-relative path including the \".md\" extension (e.g. \"Projects/notes.md\"). Parent folders are created as needed. Use the exact letter case; a different case can create a duplicate note or folder."
  3. 1 tool updatev0.54.1
    • Changedvault_get_memory2 fields changed
      • changedInput schema / properties / on_or_after / description
        Previous value: -"Inclusive date filter (YYYY-MM-DD). When provided with file and section, returns structured JSON entries dated on or after this date instead of raw markdown. Requires both file and section."New value: +"Inclusive date filter (YYYY-MM-DD). When provided with file, returns structured JSON entries dated on or after this date instead of raw markdown. Requires a file; add section to scope to one H2 section."
      • changedInput schema / properties / section / description
        Previous value: -"H2 section heading (e.g. \"Decision heuristics (newest first)\"). Matched case-insensitively, with or without the \"(newest first)\" suffix. Call vault_list_memory_files first to discover valid names."New value: +"H2 section heading (e.g. \"Decision heuristics (newest first)\"). Matched case-insensitively, with or without the \"(newest first)\" suffix. Omit to read the whole file (with on_or_after, every H2 section's entries). Call vault_list_memory_files first to discover valid names."
  4. 2 tool updatesv0.54.0
    • Changedvault_create_task1 field changed
      • changedInput schema / properties / subtasks / description
        Previous value: -"Checklist item descriptions — created as indented [ ] lines under the card (no metadata). For full sub-tasks with dates, priority, and block_id, make a separate call with parent_block_id."New value: +"Checklist item descriptions — created as indented todo lines under the card (no metadata). For full sub-tasks with dates, priority, and block_id, make a separate call with parent_block_id."
    • Changedvault_update_task1 field changed
      • changedInput schema / properties / add_subtasks / description
        Previous value: -"Checklist items to append, one indented [ ] line each, under the task's existing items — never replaces them. Can be combined with any other change; not appended when the same call removes the task (on_completion delete). For full sub-tasks with metadata, use vault_create_task with parent_block_id."New value: +"Checklist items to append, one indented todo line each, under the task's existing items — never replaces them. Can be combined with any other change; not appended when the same call removes the task (on_completion delete). For full sub-tasks with metadata, use vault_create_task with parent_block_id."
  5. 4 tool updatesv0.53.0
    • Changedvault_create_task4 fields changed
      • addedInput schema / properties / position / anyOf
        Added value: +[
        +  {
        +    "enum": [
        +      "top",
        +      "bottom"
        +    ],
        +    "type": "string"
        +  },
        +  {
        +    "maximum": 9007199254740991,
        +    "minimum": 1,
        +    "type": "integer"
        +  }
        +]
      • changedInput schema / properties / position / description
        Previous value: -"Where within the heading section the task is placed. Defaults to bottom. Kanban boards with new-card-insertion-method set to prepend default to top instead. Ignored when no heading or when placing under a parent."New value: +"Where within the heading section the task is placed. \"top\" or \"bottom\" for the extremes; an integer (1-based) for an exact position among the lane's top-level cards (sub-tasks move with their parent and are not counted). Position 1 is the first card. A position past the card count lands directly below the last card (unlike \"bottom\", which appends after any trailing section content). Defaults to bottom. Kanban boards with new-card-insertion-method set to prepend default to top instead. Ignored when no heading or when placing under a parent."
      • removedInput schema / properties / position / enum
        Removed value: -[
        -  "top",
        -  "bottom"
        -]
      • removedInput schema / properties / position / type
        Removed value: -"string"
    • Changedvault_get_memory1 field changed
      • addedInput schema / properties / on_or_after
        Added value: +{
        +  "description": "Inclusive date filter (YYYY-MM-DD). When provided with file and section, returns structured JSON entries dated on or after this date instead of raw markdown. Requires both file and section.",
        +  "minLength": 1,
        +  "type": "string"
        +}
    • Changedvault_read_note1 field changed
      • changedInput schema / properties / outline / description
        Previous value: -"If true, returns { leading_callout?, leading_content?, headings } as JSON instead of body content — a cheap structure fetch for large notes. headings: [{ level, text, bytes }]; leading_callout: { type, title, body } when the note has a top-of-file callout; leading_content: the rest of the body text above the first heading (callout lines excluded) when the note has any."New value: +"If true, returns { bytes, modified, leading_callout?, leading_content?, headings } as JSON instead of body content — a cheap structure fetch for large notes. headings: [{ level, text, bytes }]; leading_callout: { type, title, body } when the note has a top-of-file callout; leading_content: the rest of the body text above the first heading (callout lines excluded) when the note has any."
    • Changedvault_update_task5 fields changed
      • addedInput schema / properties / position / anyOf
        Added value: +[
        +  {
        +    "enum": [
        +      "top",
        +      "bottom"
        +    ],
        +    "type": "string"
        +  },
        +  {
        +    "maximum": 9007199254740991,
        +    "minimum": 1,
        +    "type": "integer"
        +  }
        +]
      • removedInput schema / properties / position / default
        Removed value: -"top"
      • changedInput schema / properties / position / description
        Previous value: -"Where within the target heading the task lands after a heading move or auto-done-lane move. Defaults to \"top\". Ignored when no heading move occurs."New value: +"Where within the target heading the task lands after a heading move or auto-done-lane move. \"top\" or \"bottom\" for the extremes; an integer (1-based) for an exact position among the lane's top-level cards (sub-tasks move with their parent and are not counted). Position 1 is the first card. A position past the card count lands directly below the last card (unlike \"bottom\", which appends after any trailing section content). Defaults to \"top\" on heading moves. Without a heading, triggers a same-lane reorder to the given position; omitting position entirely performs no reorder. Ignored when the task is deleted on completion. Not valid on sub-tasks."
      • removedInput schema / properties / position / enum
        Removed value: -[
        -  "top",
        -  "bottom"
        -]
      • removedInput schema / properties / position / type
        Removed value: -"string"
  6. 2 tool updatesv0.51.1
    • Changedvault_create_task1 field changed
      • addedInput schema / properties / on_completion
        Added value: +{
        +  "description": "Tasks plugin 🏁 onCompletion action. \"delete\" removes the task line on completion; \"keep\" leaves it in place.",
        +  "enum": [
        +    "delete",
        +    "keep"
        +  ],
        +  "type": "string"
        +}
    • Changedvault_update_task3 fields changed
      • changedInput schema / properties / add_subtasks / description
        Previous value: -"Checklist items to append, one indented [ ] line each, under the task's existing items — never replaces them. Can be combined with any other change. For full sub-tasks with metadata, use vault_create_task with parent_block_id."New value: +"Checklist items to append, one indented [ ] line each, under the task's existing items — never replaces them. Can be combined with any other change; not appended when the same call removes the task (on_completion delete). For full sub-tasks with metadata, use vault_create_task with parent_block_id."
      • addedInput schema / properties / on_completion
        Added value: +{
        +  "anyOf": [
        +    {
        +      "enum": [
        +        "delete",
        +        "keep"
        +      ],
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Tasks plugin 🏁 onCompletion action to set, or null to remove it. \"delete\" removes the task line on completion; \"keep\" leaves it in place (which is also the behavior when no 🏁 field exists on the task). Omitting this parameter leaves the field unchanged."
        +}
      • changedInput schema / properties / status / description
        Previous value: -"Target status. \"done\" appends the ✅ date and, on a Kanban board, moves the card and its checklist sub-items to the done lane (sub-item checkboxes are left as they are). \"cancelled\" appends the ❌ date."New value: +"Target status. \"done\" appends the ✅ date and, on a Kanban board, moves the card and its checklist sub-items to the done lane (sub-item checkboxes are left as they are); a task with 🏁 delete / [onCompletion:: delete] is removed from the file instead. \"cancelled\" appends the ❌ date."
  7. 2 tool updatesv0.51.0
    • Changedvault_create_task1 field changed
      • addedInput schema / properties / recurrence
        Added value: +{
        +  "description": "Tasks plugin 🔁 rule in natural language (e.g. \"every week\", \"every 2 weeks when done\"). Completing the task spawns its next occurrence.",
        +  "minLength": 1,
        +  "type": "string"
        +}
    • Changedvault_update_task1 field changed
      • addedInput schema / properties / recurrence
        Added value: +{
        +  "anyOf": [
        +    {
        +      "minLength": 1,
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Tasks plugin 🔁 rule in natural language (e.g. \"every week\", \"every 2 weeks when done\") to set, or null to remove it. Completing the task spawns its next occurrence."
        +}
  8. 17 tool updatesv0.50.0
    • Changedvault_delete_span1 field changed
      • addedInput schema / properties / first_match / default
        Added value: +false
    • Changedvault_find_orphans4 fields changed
      • addedInput schema / properties / limit / default
        Added value: +50
      • addedInput schema / properties / limit / maximum
        Added value: +9007199254740991
      • addedInput schema / properties / limit / minimum
        Added value: +1
      • changedInput schema / properties / limit / type
        Previous value: -"number"New value: +"integer"
    • Changedvault_insert_at_anchor1 field changed
      • addedInput schema / properties / first_match / default
        Added value: +false
    • Changedvault_list_files1 field changed
      • addedInput schema / properties / limit / default
        Added value: +50
    • Changedvault_list_property_values4 fields changed
      • addedInput schema / properties / limit / default
        Added value: +50
      • addedInput schema / properties / limit / maximum
        Added value: +9007199254740991
      • addedInput schema / properties / limit / minimum
        Added value: +1
      • changedInput schema / properties / limit / type
        Previous value: -"number"New value: +"integer"
    • Changedvault_list_tasks4 fields changed
      • addedInput schema / properties / limit / default
        Added value: +50
      • addedInput schema / properties / sort_by / default
        Added value: +"due"
      • addedInput schema / properties / status / default
        Added value: +"not_done"
      • addedInput schema / properties / top_level_only / default
        Added value: +false
    • Changedvault_memory_recall2 fields changed
      • addedInput schema / properties / limit
        Added value: +{
        +  "default": 50,
        +  "description": "Cap on returned entries (default 50). When more match, the least-relevant are dropped and truncated=true — never a date range.",
        +  "maximum": 9007199254740991,
        +  "minimum": 1,
        +  "type": "integer"
        +}
      • removedInput schema / properties / max_results
        Removed value: -{
        -  "description": "Cap on returned entries (default 50). When more match, the least-relevant are dropped and truncated=true — never a date range.",
        -  "type": "number"
        -}
    • Changedvault_recent_notes5 fields changed
      • addedInput schema / properties / limit / default
        Added value: +20
      • addedInput schema / properties / limit / maximum
        Added value: +9007199254740991
      • addedInput schema / properties / limit / minimum
        Added value: +1
      • changedInput schema / properties / limit / type
        Previous value: -"number"New value: +"integer"
      • addedInput schema / properties / sort_by / default
        Added value: +"modified"
    • Changedvault_replace_in_note1 field changed
      • addedInput schema / properties / replace_all_occurrences / default
        Added value: +false
    • Changedvault_replace_span1 field changed
      • addedInput schema / properties / first_match / default
        Added value: +false
    • Changedvault_search6 fields changed
      • removedInput schema / properties / filters / properties / include_leading_callout
        Removed value: -{
        -  "description": "If true, each result includes its leading_callout ({ type, title, body }) when present. Off by default to keep results lean.",
        -  "type": "boolean"
        -}
      • removedInput schema / properties / filters / properties / limit
        Removed value: -{
        -  "description": "Max results (default 20)",
        -  "type": "number"
        -}
      • removedInput schema / properties / filters / properties / snippet_tokens
        Removed value: -{
        -  "description": "Snippet length in tokens (default 30)",
        -  "type": "number"
        -}
      • addedInput schema / properties / include_leading_callout
        Added value: +{
        +  "default": false,
        +  "description": "If true, each result includes its leading_callout ({ type, title, body }) when present. Off by default to keep results lean.",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / limit
        Added value: +{
        +  "default": 20,
        +  "description": "Max results (default 20)",
        +  "maximum": 9007199254740991,
        +  "minimum": 1,
        +  "type": "integer"
        +}
      • addedInput schema / properties / snippet_tokens
        Added value: +{
        +  "default": 30,
        +  "description": "Snippet length in tokens (default 30)",
        +  "maximum": 9007199254740991,
        +  "minimum": 1,
        +  "type": "integer"
        +}
    • Changedvault_search_by_folder5 fields changed
      • addedInput schema / properties / limit / default
        Added value: +20
      • addedInput schema / properties / limit / maximum
        Added value: +9007199254740991
      • addedInput schema / properties / limit / minimum
        Added value: +1
      • changedInput schema / properties / limit / type
        Previous value: -"number"New value: +"integer"
      • addedInput schema / properties / recursive / default
        Added value: +true
    • Changedvault_search_by_property4 fields changed
      • addedInput schema / properties / limit / default
        Added value: +20
      • addedInput schema / properties / limit / maximum
        Added value: +9007199254740991
      • addedInput schema / properties / limit / minimum
        Added value: +1
      • changedInput schema / properties / limit / type
        Previous value: -"number"New value: +"integer"
    • Changedvault_search_by_tag1 field changed
      • addedInput schema / properties / exact / default
        Added value: +false
    • Changedvault_update_memory1 field changed
      • addedInput schema / properties / options / properties / position / default
        Added value: +"top"
    • Changedvault_update_task1 field changed
      • addedInput schema / properties / position / default
        Added value: +"top"
    • Changedvault_write_note1 field changed
      • addedInput schema / properties / overwrite / default
        Added value: +false

TDQS

A4.2/5.0

Scored across 33 tools

Disambiguation3/5

The tool set has several overlapping clusters: five body-editing tools (vault_patch_note, vault_replace_in_note, vault_delete_span, vault_replace_span, vault_insert_at_anchor) and six discovery/search tools all target similar user intents. Descriptions are exhaustive and contain explicit 'Prefer X for Y' guidance, which mitigates misselection, but an agent still faces real choices among many narrowly differentiated options.

Naming Consistency4/5

All tools are snake_case with a consistent 'vault_' prefix, and most follow a verb_noun pattern (e.g. vault_delete_note, vault_list_tasks, vault_create_task). Minor deviations exist: vault_search, vault_recent_notes, and vault_memory_recall break the verb_noun convention, keeping this from a perfect score.

Tool Count2/5

At 33 tools, the set exceeds the 25+ threshold that the rubric treats as too many. Although the Obsidian vault domain is broad (notes, memory, tasks, search, files, daily notes), several editing and search tools could be consolidated without losing capability.

Completeness4/5

Coverage is strong: notes have full lifecycle support (create, read, update, move, delete, properties), plus search, backlinks, outgoing links, orphans, tags, property discovery, memory, tasks, daily notes, and file listing/reading. Minor gaps remain, such as no tool to delete or write non-markdown files (e.g. canvases, attachments) and no explicit folder creation/deletion.

Maintenance

ActivityActive
ResponsivenessResponsive

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    A server that enables AI agents to perform sophisticated knowledge discovery and analysis across Obsidian vaults through the Local REST API plugin, supporting complex multi-step workflows with advanced filtering and full content retrieval.
    3
    21
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    A third-party MCP server for interacting with HashiCorp Vault to manage ACL policies, audit devices, and secret engines like KV v2, PKI, and Transit. It provides tools for system backend administration and includes prompts for generating security policy configurations.
    MIT