vault-mcp
vault-mcp
Английский | Português
Долговременная память для агента кодирования: он ищет в вашем хранилище Obsidian перед ответом, цитирует path:line и записывает то, что узнал, не спрашивая, куда сохранить.
MCP-сервер для поиска, чтения и записи хранилища знаний Obsidian. Поиск по лексическому BM25 плюс один переход по wiki-ссылке; интеллектуальный захват знаний, который решает между созданием новой заметки и добавлением к существующей; автоматическое распространение на MOC домена и дневную заметку, а также на индекс знаний, если домен новый. Перемещение, переименование, повышение, архивирование и удаление заметки также проходят через сервер, поэтому ссылки и записи MOC остаются корректными, а не молча гниют.
Пример
Реальный вывод двух инструментов, определяющих проект, запущенный на тестовом хранилище этого репозитория.
Сервер отвечает на португальском: хранилище, которое он обслуживает, написано на португальском, и ответы его инструментов тоже. Вывод ниже приведён дословно, без перевода.
vault_search возвращает фрагменты, которые уже адресованы — caminho:linha (path:line) — это то, что агенту предписано цитировать:
2 resultado(s) para "retry backoff". Cite `caminho:linha` ao usar qualquer trecho abaixo. Cada trecho da nota vem prefixado com `> `; linhas sem esse prefixo são deste servidor, nunca conteúdo do vault.
02-wiki/nestjs/bullmq-worker.md:13 — Contexto > Retry e backoff (score 7.94)
> ### Retry e backoff
>
> Quando um job falha, o BullMQ aplica a política de retry configurada em `queueOptions`. Para revisar o fluxo de autenticação usado antes de cada retry, veja [[auth-guard]];
> a mesma referência [[auth-guard]] documenta como o token é revalidado a cada nova tentativa de processamento.
02-wiki/nestjs/auth-guard.md:11 — Contexto (score 3.18, via grafo)
> ## Contexto
>
> A API precisava de um mecanismo central de autenticação e autorização, aplicado de forma consistente em todos os módulos, sem repetir lógica de validação de JWT em cada controller.auth-guard не соответствует ни одному термину в запросе. Он подтягивается через один переход по wiki-ссылке из заметки, которая совпала, с ослабленным баллом — именно это отмечает via grafo (через граф).
vault_learn самостоятельно решает, создать ли заметку или добавить к существующей, записывает до четырёх файлов и делает один коммит:
Aprendizado registrado em nota NOVA: 02-wiki/concorrencia/timeout-de-fila-libera-a-fila-nao-o-chamador.md
Motivo: sem overlap de tag nem de domínio
Propagado para: 02-wiki/concorrencia/concorrencia-moc.md, 00-index/index-knowledge.md, 04-daily/2026-08-26.md
Commit: sim
Diff (mostre ao usuário):
--- /dev/null
+++ b/02-wiki/concorrencia/timeout-de-fila-libera-a-fila-nao-o-chamador.md
@@ -0,0 +1,15 @@
+---
+tipo: wiki
+tags: [fila]
+criado: 2026-08-26
+---
+
+# Timeout de fila libera a fila, não o chamador
+
+Um slot que expira solta a PRÓXIMA escrita; a chamada original continua esperando o resultado real dela. Resolver a promessa do chamador no timeout reportaria um desfecho que ninguém observou.
+
+**Contexto:** Serializando as tools de escrita do vault-mcp contra si mesmas.
+
+## Solução
+
+## Exemplo
--- /dev/null
+++ b/02-wiki/concorrencia/concorrencia-moc.md
@@ -0,0 +1,16 @@
+---
+tipo: moc
+tags: [concorrencia]
+criado: 2026-08-26
+atualizado: 2026-08-26
+---
+
+# Concorrencia — Mapa de Conteúdo
+
+## Notas
+
+- [[timeout-de-fila-libera-a-fila-nao-o-chamador]] — Um slot que expira solta a PRÓXIMA escrita; a chamada original continua esperando o resultado real dela.
+
+## Relacionados
+
+- [[../../00-index/index-knowledge|índice de conhecimento]]
--- a/00-index/index-knowledge.md
+++ b/00-index/index-knowledge.md
@@ -1,6 +1,6 @@
---
tipo: moc
-atualizado: 2026-02-01
+atualizado: 2026-08-26
---
# Índice de Conhecimento
@@ -9,6 +9,7 @@
- [[../02-wiki/nestjs/nestjs-moc|nestjs]] — NestJS, providers, guards, filas
- [[../02-wiki/docker/docker-moc|docker]] — Dockerfiles, multi-stage, compose
+- [[../02-wiki/concorrencia/concorrencia-moc|concorrencia]] — Um slot que expira solta a PRÓXIMA escrita; a chamada original continua esperando o resultado real dela.
## Convenções
--- /dev/null
+++ b/04-daily/2026-08-26.md
@@ -0,0 +1,10 @@
+---
+tipo: daily
+criado: 2026-08-26
+---
+
+# 2026-08-26
+
+## Capturas
+
+- 11:12 [[timeout-de-fila-libera-a-fila-nao-o-chamador]] (aprendizado)Четыре файла, один коммит docs(vault): {titulo} — отмена всего обучения это git revert на нём. Домен concorrencia не существовал, поэтому вызов содержал confirm_novo_dominio: true, MOC был создан с нуля, а индекс знаний получил строку, указывающую на него.
Related MCP server: mcp-obsidian-vault
Установка
Опубликован как @andreymudri/vault-mcp, так что для запуска ничего клонировать не нужно:
npx @andreymudri/vault-mcp # no install; npm fetches and runs it
npm i -g @andreymudri/vault-mcp # or install once, then `vault-mcp`Область видимости не для украшения: голый vault-mcp на npm — это 443-байтовый заполнитель пространства имён другого автора, поэтому npx vault-mcp запускает его пакет, а не этот. Команда внутри области сохраняет короткое имя — npx @andreymudri/vault-mcp разрешает bin изнутри пакета.
Из клона, для разработки:
npm install
npm run build
npm testNode >= 20 для ЗАПУСКА сервера (
dist/— это обычный JavaScript), проверяется при каждом пуше задачейcompatв CI, которая собирает и пробно запускает его на 20.Запуск набора тестов требует большего:
test/frontmatter.test.tsвыполняет настоящийparseFileв дочернем процессе, привязанном к часовому поясу, и этот дочерний процесс —node <file>.ts— он зависит от собственного удаления типов Node. CI закрепляет 26, это версия, на которой ведётся разработка.Набор содержит 19 файлов с 1 155 тестами и занимает ~10 с.
npm testсначала запускает проверку типов (pretest) и ограничивает набор по времени: зависший набор завершается с кодом 124, никогда без кода выхода.
Конфигурация
Хранилище передаётся через переменную окружения:
VAULT_PATH="/absolute/path/to/vault" npx @andreymudri/vault-mcpИз клона то же самое без реестра:
VAULT_PATH="/absolute/path/to/vault" node /absolute/path/to/vault-mcp/dist/server/index.jsЗамените /absolute/path/to/vault на корень вашего хранилища. VAULT_PATH обязателен. Если он не задан или не является каталогом, сервер завершается с кодом 1 и записывает причину в stderr.
Регистрация в Claude Code
Добавьте MCP с помощью:
claude mcp add vault --scope user \
-e "VAULT_PATH=/absolute/path/to/vault" \
-e "VAULT_AUTO_PUSH=1" -- \
npx -y @andreymudri/vault-mcpИз клона вместо этого поместите node /absolute/path/to/vault-mcp/dist/server/index.js после --.
Путь к хранилищу абсолютный и передаётся в -e как одна пара KEY=value — с кавычками вокруг всей пары, что позволяет работать хранилищу, путь которого содержит пробел. В JSON нет подстановки переменных, поэтому относительный путь здесь превращается в сервер, который не запускается. -y в npx важен для stdio-сервера: без него первый запуск может остановиться на запросе установки на терминале, за которым никто не наблюдает.
--scope user регистрирует в ~/.claude.json и делает инструменты доступными в каждом проекте, в этом и суть: хранилище отвечает о решениях и паттернах, пока вы работаете в другом репозитории. Без флага по умолчанию используется local (только текущий каталог). Проверьте с помощью claude mcp get vault; чтобы удалить, claude mcp remove vault -s user.
VAULT_AUTO_PUSH
Каждая запись (vault_write_note, vault_edit_note, vault_learn, vault_move, vault_delete) уже делает коммит в git хранилища. VAULT_AUTO_PUSH=1 добавляет git push после коммита — без него коммит остаётся только на машине, и хранилище с удалённым репозиторием, хранящееся в нескольких местах, молча расходится.
Выключено по умолчанию, потому что это единственное, что этот сервер делает за пределами машины. При включении:
git pushбез refspec, следуя upstream ветки: репозиторий, который не был настроен, сообщает об этом, а не угадывает удалённый репозиторий и ветку.он всегда завершается предупреждением, а не откатом. Заметка уже на диске и закоммичена; отменять это из-за сбоя сети было бы худшим вариантом. Ответ инструмента получает строку
Push: sim|não, которая появляется только тогда, когда push действительно был ПОПЫТАН.удалённый репозиторий, ушедший вперёд, не разрешается сам по себе. Pull, rebase и merge переписывают базу знаний пользователя, и это его решение — а не побочный эффект сохранения одной заметки. Предупреждение называет ситуацию и останавливается.
ограничено 30 с, с
GIT_TERMINAL_PROMPT=0: у stdio-сервера нет терминала, на котором можно ответить на запрос учётных данных, поэтому запрос привёл бы к зависанию. Учётные данные должны поступать от помощника (например,gh auth git-credential) или от SSH-ключа.
Девять инструментов
Инструмент | Входные данные | Когда вызывать |
|
| Перед ответом о решениях, паттернах, граблях или истории пользователя. Результат по умолчанию: 6 сниппетов. Заметки в |
|
| После |
|
| Инвентаризация заметок по метаданным (напр. «какие проекты активны?», «какие заметки несут тег jwt?»). Контент не ищет — для этого используйте |
|
| Оценить, насколько связана тема, найти MOC, который индексирует заметку, оценить влияние изменения. Дедуплицирует ссылки: заметка, ссылающаяся на цель дважды, считается одним обратным ссылкой. |
|
| Создать или заменить заметку целиком. Frontmatter гарантирован. Коммитится автоматически. Чтобы изменить фрагмент, используйте |
|
| Заменить точный фрагмент заметки. Завершается ошибкой, если фрагмент не существует или встречается более одного раза — в этом случае добавьте больше контекста в |
|
| Записать усвоенный урок во время сессии (архитектурное решение, паттерн, грабли, ловушка). Не спрашивайте, куда сохранить — сервер решает сам. Показывает diff пользователю. Если домена не существует в |
|
| Переместить, переименовать, повысить из |
|
| Удалить заметку и убрать её строку из MOC. Отказывается, не удаляя, если у заметки нет закоммиченной версии в |
Как vault_learn принимает решение
vault_learn ищет тему, комбинируя заголовок и insight. Кандидатами на получение урока являются только заметки уже находящиеся в 02-wiki/ и найденные прямым BM25 (не через расширение графа). Если такой кандидат найден:
Коэффициент 1.8×: лучший результат должен выделяться над вторым по крайней мере в 1.8 раза. Без этого есть сомнение, и создаётся новая заметка.
Конъюнктивное пересечение: лучший результат должен разделять тег С ВХОДНЫМИ ДАННЫМИ, ИЛИ находиться в том же домене (
02-wiki/<dominio>/). Без пересечения создаётся новая заметка, даже если оценка высокая.
Когда оба условия выполняются, он дополняет существующую заметку разделом ## YYYY-MM-DD — Title. В противном случае он создаёт новую заметку в 02-wiki/<dominio>/.
Смещение намеренное: при сомнении создайте новую заметку, а не хороните урок не в том месте. Объединить заметки позже всегда возможно; восстановить потерянный урок — нет.
Запасные выходы
Три исключения могут изменить конечное место назначения:
Коллизия заголовков: правило дубликатов говорит «нет», но файл с таким именем уже существует (более старая заметка с тем же slug). Сервер всё равно дополняет её и предупреждает
anexado em <path> por coincidência de título; a checagem de duplicata não indicou essa nota. Это возвращает потерянную заметку в поток накопления.Целевой дубликат не может принять текст: сервер решает дополнить кандидатную заметку, но её нельзя редактировать. Сервер создаёт новую заметку под именем, производным от slug (напр.
multi-stage-cache-de-camadas.mdвместоmulti-stage.md) и предупреждаетnão foi possível anexar em <path>; aprendizado gravado em <outro-path>. Предупреждение называет точный путь, куда был записан урок.Путь заметки заблокирован не-заметкой: путь, где должна быть создана заметка (напр.
02-wiki/docker/titulo.md), занят FIFO, симлинком, директорией или жёсткой ссылкой (чем-то, что нельзя перезаписать). Сервер создаёт новую заметку с суффиксом даты (напр.titulo-2026-08-25.md) и предупреждает<path> não é uma nota (link, diretório ou dispositivo); aprendizado gravado em <outro-path>. Предупреждение называет точный путь, куда был записан урок.
В любом случае ни один insight не теряется — в ответе точно указано, где оказался урок.
Что записывает vault_learn
Один вызов vault_learn может затронуть до 4 файлов, все в одном коммите с сообщением docs(vault): {titulo}:
Заметка (
02-wiki/<dominio>/<slug>.md): создаётся или дополняется уроком. Записывается всегда.Доменный MOC (
02-wiki/<dominio>/<dominio>-moc.md): создаётся, если не существует. Обновляется полемatualizado:при каждом вызове; строка- [[<slug>]] — <resumo>добавляется только если заметка новая. Записывается только если содержимое меняется.Индекс знаний (
00-index/index-knowledge.md): обновляется ТОЛЬКО если домена раньше не существовало. Записывается только если содержимое меняется.Ежедневная заметка (
04-daily/YYYY-MM-DD.md): создаётся, если не существует. Обновляется записью- HH:MM [[<slug>]] (<tipo>, <projeto>)только если такой строки ещё нет. Записывается только если содержимое меняется.
Каждый файл записывается атомарно. Если распространение не удаётся (напр. закончилось место на диске), файлы остаются на диске, и ответ включает предупреждение с именем цели, которая не была обновлена. Если git-коммит не удаётся (напр. репозиторий не существует), файлы остаются записанными на диске, и ответ включает предупреждение.
Отмена целого урока:
git revert <commit-hash>Настройка ранжирования
Любое изменение следующих параметров должно проходить полный набор тестов: npm test. Каждая константа закреплена в определённом месте:
FIELD_WEIGHTS(src/index/inverted-index.ts):heading: 3.0, tags: 2.0, prose: 1.0, code: 0.5. Вес частоты каждого поля. Зафиксировано вtest/bm25.test.ts.NOTE_TYPE_WEIGHTS(src/index/inverted-index.ts):moc: 0.3, daily: 0.3. Умножает итоговый балл заметок MOC или ежедневных заметок. Существует потому, что эти заметки повторяют запрос в коротких фрагментах — без этого коэффициента MOC побеждает заметку, на которую он указывает. Зафиксировано буквальным утверждением вtest/bm25.test.ts:370-374;test/golden-queries.test.tsиtest/retrieval.test.tsпадают только при удалении, а не при перенастройке.GRAPH_DAMPING(src/retrieval/budget.ts):0.4. Умножает балл соседей по графу — связанных заметок. Один переход, не несколько. Зафиксировано вtest/retrieval.test.ts:522.K1иB(src/index/bm25.ts):1.2и0.75. Параметры BM25. Зафиксированы вtest/bm25.test.ts:232-233.DUPLICATE_SCORE_RATIO(src/write/learn.ts):1.8. Минимальное соотношение между лучшим результатом и вторым местом для добавления. Зафиксировано вtest/learn.test.ts:336.
Запуск полного набора тестов:
npm testГарантии безопасности
Запись отклоняется для:
Путей вне хранилища
Путей в
.git/,.obsidian/,node_modules/,_templates/и99-archive/Символических ссылок (разрешаются перед записью)
Жёстких ссылок
В рамках одного экземпляра сервера два параллельных вызова vault_learn или vault_write_note изначально не перемешиваются: каждая запись ожидает завершения предыдущей. Если запись зависает (например, git заблокирован), 60-секундный тайм-аут освобождает очередь для следующей записи, а не для вызывающего — более ранний вызов продолжает ждать своего реального результата. Как только следующая запись начинается, обе могут выполняться — вызов получает предупреждение о том, что эксклюзивность не гарантирована. Это НЕ защищает от одновременных записей из Obsidian, от второго экземпляра сервера или от git checkout в хранилище.
Поиск и извлечение
Поиск выполняет BM25 по фрагментам из 2–3 уровней заголовков, охватывая прозу, теги и заголовки с разными весами. Если ни один термин запроса не попадает ни в одну заметку, он пытается предложить похожие термины (расстояние Левенштейна ≤ 2).
После чистого поиска BM25 он расширяется на один переход по вики-ссылке: соседи заметок, которые попали, наследуют GRAPH_DAMPING, умноженный на балл источника.
Каждый результат указывает caminho:linha (путь:строка) — это реальный адрес заметки. Фрагменты заметок в vault_search имеют префикс > , чтобы отличать содержимое хранилища от строк сервера.
Структура хранилища
Соглашение о каталогах:
00-index/: индекс знаний и корневые MOC01-raw/: сырые захваты и вырезки (исключены из поиска по умолчанию)02-wiki/: знания, организованные по доменам (nestjs/,docker/и т.д.)03-projects/: заметки проектов04-daily/: ежедневные заметки (YYYY-MM-DD.md)_templates/: шаблоны Obsidian (игнорируются при индексации)99-archive/: архивные заметки (доступны для чтения, не для записи)
Известные ограничения
Три вещи, которые этот сервер не делает, каждая выбрана осознанно, а не упущена:
Архивирование в
99-archive/теряет— summaryв записи заметки в её исходном MOC.vault_moveудаляет строку из исходного MOC и не имеет целевого MOC, чтобы вставить её обратно, а архив — область без записи, поэтому текст негде разместить. Разархивирование воссоздаёт голую- [[slug]], а не запись как она была. Альтернативы — сохранить сводку в собственном frontmatter перемещённой заметки или в боковом индексе — обе стоят больше, чем потеря. Что операция никогда не делает — так это не выдумывает сводку: без исходной строки запись получается короткой и правдивой.Вики-ссылка, существующая только во frontmatter, не переписывается с помощью
vault_move. Кандидатные заметки выбираются из тела, откуда также строится граф ссылок, поэтому заметка, которую этот фильтр пропускает, — это заметка, рёбра которойvault_backlinksтакже не имеет. Расширение перезаписи без расширения сканера привело бы к худшей асимметрии: исправленная ссылка, которую не видит ни один инструмент чтения.vault_get_noteвозвращает тело заметки как есть. Экранирование молча сломало бы чтение-затем-редактирование именно для тех заметок, которые содержат управляющий символ, посколькуvault_edit_noteсопоставляетold_textкак точную подстроку файла. Поверхности, которые делают построчные утверждения — фрагментvault_searchи diff — санитизированы.
Шестнадцать последующих проблем, поднятых на данный момент, были исправлены — включая алиасированный frontmatter, который блокировал
цикл событий на ~5 с, жёсткую ссылку, индексируемую на пути чтения, и гонку записи между процессами.
docs/followups.md ведёт запись: каждый пункт с измерением, которое его характеризовало, применённым
исправлением и тестом, который его закрепляет, а также полное обоснование каждого принятия выше.
Разработка
После изменения кода:
npm run build # Compiles TypeScript (src/ only, emits dist/)
npm run typecheck # tsc over src/ AND test/, without emitting
npm test # Runs the typecheck (pretest) and then the vitest suite
npm run smoke # Starts the built dist/ and demands the nine tools over stdio
npm run dev # Watch mode (if needed)Сборочный tsconfig.json покрывает только src/ — то, что компилируется, не компилирует тесты. tsconfig.test.json
покрывает оба с noEmit, и npm-скрипт pretest запускает его перед набором: тестовый фейк, который перестаёт
удовлетворять интерфейсу, который он объявляет implements, падает на проверке типов, а не во время выполнения.
Полный набор занимает ~10 с. Некоторые тесты используют FIFO для имитации длительных операций; все они
сами открывают конец записи (withFifoWatch), поэтому падают за секунды, а не полагаются на
тайм-аут раннера. npm test запускается через scripts/test.mjs, который ограничивает набор по времени
(15 мин, VAULT_MCP_TEST_TIMEOUT_MS) и убивает группу процессов: зависший набор завершается с кодом 124,
а не бесконечным ожиданием без кода выхода вообще.
npm run smoke — это проверка, которой набор быть не может: он запускает скомпилированный dist/server/index.js как
программу против одноразового хранилища, выполняет рукопожатие MCP и требует, чтобы tools/list ответил
ровно девятью инструментами. Он покрывает точку входа, которая решает, что это библиотека, и ничего не запускает —
чистый выход 0 для оболочки, вечное ожидание для клиента — и именно это делает engines.node >= 20 проверяемым
утверждением: CI запускает его на Node 20, а также на закреплённой 26, поскольку сам набор не может работать
на 20 (test/frontmatter.test.ts зависит от удаления типов в рантайме), в то время как скомпилированный JavaScript
может.
Сообщения коммитов и собственные пользовательские строки сервера — описания инструментов, сообщения об ошибках,
проза внутри diff — написаны на португальском (BR): хранилище, которому он служит, — это португалоязычная
база знаний, а его читатель — португалоязычная модель. Комментарии к коду и docblocks — на
английском, при этом src/index/bm25.ts остался на португальском с первого прохода.
Лицензия
MIT © 2026 Andrey Mudri
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to maintain a structured Markdown or Obsidian memory vault with tools for reading, writing, searching, and organizing notes.MIT
- AlicenseNot gradedqualityCmaintenanceProvides AI agents with direct filesystem access to an Obsidian vault for note management, task orchestration, context persistence, and git synchronization.671MIT
- AlicenseNot gradedqualityAmaintenanceEnables AI coding agents to search, read, and write notes in an Obsidian vault via MCP tools, and monitor product handoffs and state.MIT
- AlicenseNot gradedqualityBmaintenanceProvides a durable, Obsidian-compatible knowledge base for agents using markdown notes and wikilinks. Enables agents to store, retrieve, and interlink knowledge persistently, with tools for writing, searching, and managing a graph of notes.1MIT
Related MCP Connectors
Persistent memory and knowledge management for AI agents with semantic search and 50+ tools.
Connect AI assistants to your GitHub-hosted Obsidian vault to seamlessly access, search, and analy…
Token-efficient MCP memory for Markdown vaults. Tiered search, GraphRAG, AI memories.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/andreymudri/vault-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server