Связка решает проблему персистентной памяти агента: vault становится контекстом, который Claude читает при каждом старте, пишет в конце каждой сессии и использует для поиска нужных знаний.
Статья — полный гайд: структура vault, CLAUDE.md, три способа подключить MCP, скрипты для автоматизации frontmatter и поиска по vault через CLI.
Диагноз: в чём реально проблема
Каждая новая сессия Claude Code — чистый лист. Ты объясняешь контекст заново. Повторяешь решения которые уже принимал. Отвечаешь на вопросы которые уже задавал.
Это не баг Claude Code. Это архитектура LLM: нет сессии — нет памяти. Но это лечится.
Obsidian vault — это папка с .md файлами. Claude Code умеет читать файлы. Значит vault может быть памятью агента которая переживает любое количество сессий. Ты написал решение проблемы три месяца назад — Claude найдёт его и воспользуется. Принял архитектурное решение в пятницу — в понедельник агент знает о нём без повторного объяснения.
В статье разберём:
- Как структурировать vault под работу с Claude Code
- Что писать в CLAUDE.md чтобы агент понимал твою систему
- Три способа подключить Obsidian к Claude Code через MCP
- Скрипты для автоматизации frontmatter и метаданных
- CLI-поиск по vault без запуска Obsidian
- Хуки для автоматической записи сессий в vault
Настройка базового уровня — 30 минут. С MCP и автоматизацией — полтора часа.
Как Claude Code работает с файлами
Claude Code запускается в директории. Всё что лежит в этой директории — агент видит, читает, редактирует. Никакого API не нужно — просто файловая система.
%%{init: {
'theme': 'base',
'themeVariables': {
'primaryColor': '#ffffff',
'primaryTextColor': '#1e293b',
'primaryBorderColor': '#94a3b8',
'lineColor': '#64748b',
'fontSize': '14px',
'fontFamily': 'ui-sans-serif, system-ui, sans-serif'
},
'flowchart': {'curve': 'linear', 'nodeSpacing': 55, 'rankSpacing': 50}
}}%%
flowchart TD
CC["Claude Code (терминал)"] --> CLAUDE["CLAUDE.md - правила и контекст"]
CC --> VF["Vault файлы (.md)"]
CC --> SC["Скрипты (.py, .sh)"]
CC --> MCP["MCP сервер Obsidian"]
CLAUDE --> AG["Агент понимает структуру vault"]
VF --> AG
MCP --> SR["Семантический поиск по vault"]
AG --> WR["Пишет заметки, обновляет frontmatter"]
SR --> WR
style CC fill:#f8fafc,stroke:#3b82f6,stroke-width:2px,color:#1e40af
style CLAUDE fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d
style MCP fill:#f8fafc,stroke:#f97316,stroke-width:2px,color:#9a3412
style AG fill:#f8fafc,stroke:#64748b,stroke-width:2px,color:#1e293b
Есть три режима работы Claude Code с vault:
Режим 1: Vault как рабочая директория. Запускаешь claude прямо из папки vault. Агент видит все заметки напрямую. Просто, без настройки. Минус — если vault большой, агент будет видеть и мусор.
Режим 2: Симлинки из проекта в vault. Запускаешь Claude Code в папке проекта, делаешь симлинк на нужные части vault. Агент видит код проекта и нужные заметки одновременно.
Режим 3: MCP мост. Claude Code работает в любой директории, а к vault обращается через MCP сервер. Полное разделение проект/знания. Семантический поиск, фильтрация по frontmatter, запись новых заметок.
Начнём с основы которая нужна в любом режиме — структура vault и CLAUDE.md.
Структура vault под работу с Claude Code
Плоская структура — лучшая. Каждый лишний уровень вложенности стоит токенов при обходе директорий. Держи не глубже трёх уровней.
Стандартная структура которая работает:
vault/
00-Inbox/ <- сырые заметки, необработанные мысли
01-Projects/ <- активные проекты, по папке на проект
02-Areas/ <- зоны постоянной ответственности
03-Resources/ <- справочные материалы
04-Archive/ <- завершённые проекты, старые заметки
_claude/ <- всё что пишет агент (не твои заметки)
sessions/ <- логи сессий
summaries/ <- еженедельные итоги
decisions/ <- зафиксированные решения
Templates/ <- шаблоны для новых заметок
CLAUDE.md <- главный конфиг агента
Папка _claude/ — ключевая деталь. Туда идут все выходы агента: логи сессий, черновики, генерация. Твои собственные заметки хранятся отдельно. Это важно — иначе через месяц не поймёшь где твои мысли, а где ИИ-выхлоп.
Frontmatter как язык общения с агентом
Агент может парсить YAML frontmatter в начале .md файлов. Это его API к твоим данным. Стандартизируй frontmatter с первой заметки — потом не придётся переделывать.
Минимальный фронтматтер:
---
created: 2025-06-15
modified: 2025-06-20
tags: [project, backend, in-progress]
status: active
related: [[Architecture Decision 001]], [[API Design Notes]]
---
Поля которые реально используются в автоматизации:
- status — active, in-progress, done, archived, review
- tags — массив тегов, не строка. Строку агент иногда парсит неверно
- related — wiki-ссылки на связанные заметки
- project — к какому проекту относится
- created / modified — даты, агент умеет фильтровать по ним
CLAUDE.md: мозговой центр агента
CLAUDE.md — файл который Claude Code читает автоматически при запуске в директории где он лежит. Это не просто readme. Это оперативная память сессии: правила, контекст, структура vault, протоколы работы.
Чем точнее CLAUDE.md описывает твою систему — тем меньше вопросов агент задаёт и тем меньше ошибок делает.
Базовый шаблон CLAUDE.md для vault:
# Vault: [Название]
## Контекст
[Две строки кто ты и зачем этот vault]
Пример: Vault разработчика-фрилансера. Хранит технические решения,
заметки по проектам и учебные материалы. Не для личного дневника.
## Структура vault
- 00-Inbox/ - необработанные заметки, проверяй первым делом
- 01-Projects/ - активные проекты. Статус active в frontmatter
- 02-Areas/ - постоянные зоны ответственности
- 03-Resources/ - справочные материалы по технологиям
- 04-Archive/ - завершённое, не трогать без запроса
- _claude/ - ТВОЯ папка. Пиши сюда все выходы
- _claude/sessions/ - YYYY-MM-DD-HH.md логи сессий
- _claude/decisions/ - зафиксированные архитектурные решения
## Frontmatter стандарт
Каждая новая заметка должна содержать:
created: (сегодняшняя дата)
tags: [массив, тегов]
status: active | in-progress | done | archived
project: (название проекта если применимо)
## Протокол старта сессии
1. Прочитай _claude/sessions/ - последние 3 лога
2. Проверь 00-Inbox/ на необработанные заметки
3. Найди заметки с тегом #needs-review
## Протокол завершения сессии
1. Запиши лог в _claude/sessions/YYYY-MM-DD-HH.md
2. Обнови frontmatter изменённых заметок (modified: дата)
3. Добавь теги к новым заметкам если их нет
## Правила
- Не трогай 04-Archive без явного запроса
- Новые заметки только с frontmatter
- Wiki-ссылки в формате [[Название заметки]]
- Не переименовывай файлы без подтверждения
Это живой документ. После первых недель работы ты добавишь туда правила которые выработались из реальных сессий. Посмотри в коммит-историю vault через месяц — CLAUDE.md будет самым часто меняющимся файлом.
Протокол сессии: почему это важно
Протокол старта — это то что агент делает в первые 30 секунд. Читает логи предыдущих сессий, смотрит Inbox, проверяет незакрытые задачи. Это и есть персистентная память.
Без протокола агент начинает сессию с нуля каждый раз. С протоколом — он знает что ты делал вчера, какие решения принял на прошлой неделе и что ещё не закрыто.
Протокол завершения — зеркально. Агент пишет структурированный лог: что сделано, какие решения приняты, что осталось открытым, что нужно сделать в следующую сессию.
# Session Log: 2025-06-20 14:00
## Что делали
- Рефакторинг auth модуля - перешли на JWT
- Написали тест для refresh token flow
- Нашли баг в middleware (см. заметку [[Auth Bug 2025-06-20]])
## Решения
- JWT secret теперь из env, не из config файла
- Refresh token TTL = 30 дней (обсудили с заказчиком)
## Открыто
- [ ] Написать миграцию для старых сессий
- [ ] Обновить документацию API
## Следующая сессия
Начать с миграции старых сессий. Файл: 01-Projects/auth-refactor/migration.md
Способ 1: Vault как рабочая директория (самый простой)
Запускаешь Claude Code прямо из папки vault. Без плагинов, без настройки.
cd ~/Documents/MyVault
claude
Агент видит всю структуру. Можешь сразу работать.
Проблема — если в vault тысяча заметок, агент при поиске будет обходить всё подряд. Для vault до 500 заметок — нормально. Больше — начинает тормозить и съедать токены.
Чтобы скрыть ненужное — используй .claudeignore в корне vault:
# .claudeignore
04-Archive/
.obsidian/
Templates/
*.png
*.jpg
*.pdf
*.mp3
Формат идентичен .gitignore. Что перечислено — агент не видит.
Способ 2: Симлинки — vault рядом с проектом
Работаешь над кодовым проектом, но хочешь чтобы агент видел и заметки из vault.
# Запускаешь Claude Code в папке проекта
cd ~/projects/my-app
# Создаёшь симлинк на нужные части vault
ln -s ~/Documents/MyVault/01-Projects/my-app ./vault-notes
ln -s ~/Documents/MyVault/_claude/decisions ./decisions
ln -s ~/Documents/MyVault/03-Resources/backend ./resources
Теперь в директории проекта лежат симлинки. Claude Code видит и код, и заметки как единое рабочее пространство.
Минус симлинков — нужно создавать их для каждого проекта. Если проектов много — это рутина. Для нескольких постоянных проектов подходит, для десятков — уже лучше MCP.
Способ 3: MCP сервер — полная интеграция
MCP (Model Context Protocol) — стандарт который позволяет Claude Code обращаться к внешним инструментам. Есть несколько вариантов MCP-сервера для Obsidian.
Вариант A: obsidian-claude-code-mcp (плагин внутри Obsidian)
github.com/iansinnott/obsidian-claude-code-mcp
Устанавливается как плагин Obsidian. Поднимает MCP сервер прямо внутри приложения. Claude Code находит его автоматически через WebSocket на порту 22360.
Установка:
# Нужен Node.js
node --version # должно быть 18+
# В Obsidian: Settings - Community plugins - Browse
# Ищи: "Claude Code MCP"
# Устанавливай и включай
После включения плагин стартует MCP сервер. Claude Code при запуске в любой директории автоматически обнаруживает vault через lock-файл механизм.
# Просто запусти Claude Code
claude
# Проверь что vault обнаружен
/ide
# Должно показать подключённые MCP серверы включая Obsidian
Вариант B: mcp-obsidian через Local REST API
github.com/MarkusPfundstein/mcp-obsidian
Два компонента: плагин Obsidian (Local REST API) + MCP сервер на Python.
Шаг 1: Установи плагин Local REST API в Obsidian
Settings — Community plugins — Browse. Ищи «Local REST API». Установи, включи. Скопируй API ключ из настроек плагина.
# Плагин поднимает REST API на:
# HTTP: http://localhost:27123
# HTTPS: https://localhost:27124 (самоподписанный сертификат)
Шаг 2: Установи mcp-obsidian
# Нужен uv (менеджер пакетов Python)
curl -LsSf https://astral.sh/uv/install.sh | sh
# Установи mcp-obsidian
uv tool install mcp-obsidian
Шаг 3: Добавь конфиг в Claude Code
# Создай или отредактируй конфиг MCP
# macOS/Linux: ~/.claude/settings.json
# Windows: %APPDATA%\Claude\settings.json
{
"mcpServers": {
"obsidian": {
"command": "uvx",
"args": ["mcp-obsidian"],
"env": {
"OBSIDIAN_API_KEY": "твой-api-ключ-из-плагина",
"OBSIDIAN_HOST": "localhost",
"OBSIDIAN_PORT": "27123"
}
}
}
}
Шаг 4: Перезапусти Claude Code
claude
# В сессии: /mcp - покажет подключённые серверы
Вариант C: Filesystem MCP — самый простой
Если не хочешь ни плагинов, ни Python — есть встроенный Filesystem MCP сервер. Никаких зависимостей, просто указываешь путь к vault.
{
"mcpServers": {
"vault": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/Users/username/Documents/MyVault"
]
}
}
}
Плюс: работает без запущенного Obsidian. Минус: только чтение/запись файлов, без семантического поиска.
Сравнение трёх MCP вариантов
| Вариант | Сложность | Поиск | Frontmatter | Obsidian нужен |
|---|---|---|---|---|
| obsidian-claude-code-mcp | Низкая | Базовый | Да | Да |
| mcp-obsidian (REST API) | Средняя | Полнотекстовый | Да, парсит | Да |
| Filesystem MCP | Минимальная | Grep только | Только как текст | Нет |
Архитектура взаимодействия
%%{init: {
'theme': 'base',
'themeVariables': {
'primaryColor': '#ffffff',
'primaryTextColor': '#1e293b',
'primaryBorderColor': '#94a3b8',
'lineColor': '#64748b',
'fontSize': '14px',
'fontFamily': 'ui-sans-serif, system-ui, sans-serif'
},
'flowchart': {'curve': 'linear', 'nodeSpacing': 50, 'rankSpacing': 55}
}}%%
flowchart TD
DEV["Разработчик"] --> CC["Claude Code (терминал)"]
CC --> CM["CLAUDE.md - читается автоматически"]
CC --> MCP_A["Вариант A: obsidian-claude-code-mcp\nWebSocket :22360"]
CC --> MCP_B["Вариант B: mcp-obsidian\nREST API :27123"]
CC --> MCP_C["Вариант C: Filesystem MCP\nпрямо с диска"]
MCP_A --> OB["Obsidian (запущен)"]
MCP_B --> OB
MCP_C --> VF["Vault файлы на диске"]
OB --> VF
VF --> NM["Заметки .md"]
VF --> FM["Frontmatter YAML"]
VF --> LG["_claude/sessions/ логи"]
style CC fill:#f8fafc,stroke:#3b82f6,stroke-width:2px,color:#1e40af
style OB fill:#f8fafc,stroke:#f97316,stroke-width:2px,color:#9a3412
style VF fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d
Кейс 1: Автоматизация тегов и frontmatter
Самая частая задача — привести в порядок метаданные vault. Заметки без тегов, несогласованные статусы, битые wiki-ссылки. Вручную — часы работы. С Claude Code — скрипт плюс агент.
Скрипт валидации frontmatter
Сохрани как scripts/validate_frontmatter.py в корне vault:
#!/usr/bin/env python3
"""
validate_frontmatter.py
Проверяет frontmatter всех .md файлов в vault.
Выводит список проблемных файлов.
"""
import os
import sys
import yaml
from pathlib import Path
from datetime import datetime
VAULT_ROOT = Path(__file__).parent.parent
SKIP_DIRS = {'.obsidian', '.git', 'Templates', '.claudeignore'}
REQUIRED_FIELDS = ['tags', 'status', 'created']
VALID_STATUSES = {'active', 'in-progress', 'done', 'archived', 'review'}
issues = []
def parse_frontmatter(filepath):
"""Парсит YAML frontmatter из .md файла."""
content = filepath.read_text(encoding='utf-8')
if not content.startswith('---'):
return None, content
try:
end = content.index('---', 3)
fm_raw = content[3:end].strip()
return yaml.safe_load(fm_raw), content[end+3:]
except (ValueError, yaml.YAMLError):
return None, content
def check_file(filepath):
"""Проверяет один .md файл."""
rel_path = filepath.relative_to(VAULT_ROOT)
fm, body = parse_frontmatter(filepath)
if fm is None:
issues.append({
'file': str(rel_path),
'issue': 'NO_FRONTMATTER',
'detail': 'Frontmatter отсутствует'
})
return
# Проверяем обязательные поля
for field in REQUIRED_FIELDS:
if field not in fm:
issues.append({
'file': str(rel_path),
'issue': 'MISSING_FIELD',
'detail': f'Отсутствует поле: {field}'
})
# tags должен быть списком
if 'tags' in fm and not isinstance(fm['tags'], list):
issues.append({
'file': str(rel_path),
'issue': 'TAGS_NOT_LIST',
'detail': f'tags должен быть списком, получили: {type(fm["tags"]).__name__}'
})
# статус должен быть из допустимых значений
if 'status' in fm and fm['status'] not in VALID_STATUSES:
issues.append({
'file': str(rel_path),
'issue': 'INVALID_STATUS',
'detail': f'Недопустимый статус: {fm["status"]}. Допустимые: {VALID_STATUSES}'
})
def main():
for root, dirs, files in os.walk(VAULT_ROOT):
# Пропускаем системные папки
dirs[:] = [d for d in dirs if d not in SKIP_DIRS]
for fname in files:
if fname.endswith('.md'):
check_file(Path(root) / fname)
if not issues:
print("OK: все frontmatter валидны")
sys.exit(0)
print(f"Найдено проблем: {len(issues)}\n")
for issue in issues:
print(f"[{issue['issue']}] {issue['file']}")
print(f" {issue['detail']}\n")
sys.exit(1)
if __name__ == '__main__':
main()
# Запусти из папки vault
python3 scripts/validate_frontmatter.py
# Или попроси агента:
# "Запусти validate_frontmatter.py и исправь все найденные проблемы"
Скрипт автоматической расстановки тегов
Находит заметки без тегов, анализирует содержимое, предлагает теги на основе ключевых слов. Агент принимает решение — скрипт исполняет.
#!/usr/bin/env python3
"""
autotag.py
Предлагает теги для заметок у которых их нет.
Работает по словарю ключевых слов.
Режим --dry-run показывает что изменится без применения.
"""
import os
import re
import sys
import yaml
import argparse
from pathlib import Path
VAULT_ROOT = Path(__file__).parent.parent
SKIP_DIRS = {'.obsidian', '.git', 'Templates', '_claude'}
# Словарь: ключевое слово -> тег
KEYWORD_TAG_MAP = {
# Языки и технологии
'python': 'python',
'javascript': 'javascript',
'typescript': 'typescript',
'react': 'react',
'docker': 'docker',
'kubernetes': 'kubernetes',
'sql': 'database',
'postgres': 'database',
'mysql': 'database',
'redis': 'database',
'api': 'api',
'rest api': 'api',
'graphql': 'graphql',
'nginx': 'nginx',
'linux': 'linux',
# Типы контента
'meeting': 'meeting',
'встреча': 'meeting',
'decision': 'decision',
'решение': 'decision',
'bug': 'debugging',
'баг': 'debugging',
'refactor': 'refactoring',
'architecture': 'architecture',
'архитектура': 'architecture',
'review': 'review',
'todo': 'todo',
}
def parse_frontmatter(filepath):
content = filepath.read_text(encoding='utf-8')
if not content.startswith('---'):
return None, content, 0
try:
end = content.index('---', 3)
fm_raw = content[3:end].strip()
return yaml.safe_load(fm_raw), content[end+3:], end+3
except (ValueError, yaml.YAMLError):
return None, content, 0
def suggest_tags(text):
"""Предлагает теги на основе содержимого."""
text_lower = text.lower()
suggested = set()
for keyword, tag in KEYWORD_TAG_MAP.items():
if keyword in text_lower:
suggested.add(tag)
return list(suggested)
def update_frontmatter(filepath, new_tags, dry_run=False):
"""Добавляет теги в frontmatter файла."""
fm, body, fm_end = parse_frontmatter(filepath)
if fm is None:
fm = {}
existing_tags = fm.get('tags', [])
if isinstance(existing_tags, str):
existing_tags = [existing_tags]
merged = list(set(existing_tags + new_tags))
fm['tags'] = sorted(merged)
if dry_run:
return fm['tags']
# Записываем обновлённый файл
new_fm = yaml.dump(fm, allow_unicode=True, default_flow_style=False).strip()
content = filepath.read_text(encoding='utf-8')
if content.startswith('---'):
end = content.index('---', 3)
new_content = f"---\n{new_fm}\n---{content[end+3:]}"
else:
new_content = f"---\n{new_fm}\n---\n\n{content}"
filepath.write_text(new_content, encoding='utf-8')
return fm['tags']
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--dry-run', action='store_true',
help='Показать изменения без применения')
parser.add_argument('--threshold', type=float, default=0.0,
help='Минимальная уверенность (не используется, для совместимости)')
args = parser.parse_args()
changed = 0
for root, dirs, files in os.walk(VAULT_ROOT):
dirs[:] = [d for d in dirs if d not in SKIP_DIRS]
for fname in files:
if not fname.endswith('.md'):
continue
filepath = Path(root) / fname
fm, body, _ = parse_frontmatter(filepath)
existing_tags = []
if fm:
existing_tags = fm.get('tags', [])
if isinstance(existing_tags, str):
existing_tags = [existing_tags]
# Предлагаем теги на основе содержимого заметки
suggested = suggest_tags(body)
new_tags = [t for t in suggested if t not in existing_tags]
if not new_tags:
continue
rel_path = filepath.relative_to(VAULT_ROOT)
if args.dry_run:
print(f"[DRY RUN] {rel_path}")
print(f" Добавить: {new_tags}")
else:
result = update_frontmatter(filepath, new_tags)
print(f"[UPDATED] {rel_path}")
print(f" Теги: {result}")
changed += 1
print(f"\nИтого: {'будет изменено' if args.dry_run else 'изменено'} {changed} файлов")
if __name__ == '__main__':
main()
# Сначала dry run - посмотри что изменится
python3 scripts/autotag.py --dry-run
# Применить изменения
python3 scripts/autotag.py
# Попросить агента:
# "Запусти autotag.py --dry-run, покажи результат,
# и если теги выглядят разумно - применяй"
Кейс 2: Поиск и анализ по vault через CLI
Obsidian для поиска запускать не нужно. Vault — обычные файлы, значит grep, find и Python справляются не хуже.
Базовый поиск через grep/ripgrep
# Установи ripgrep - grep на стероидах
# macOS:
brew install ripgrep
# Ubuntu/Debian:
apt install ripgrep
# Поиск по содержимому
rg "аутентификация JWT" ~/Documents/MyVault
# Поиск с показом контекста (2 строки вокруг)
rg -C 2 "frontmatter" ~/Documents/MyVault
# Поиск только в .md файлах
rg --type md "TODO" ~/Documents/MyVault
# Поиск в конкретной папке
rg "docker" ~/Documents/MyVault/03-Resources/
# Поиск по frontmatter - все активные проекты
rg "^status: active" ~/Documents/MyVault/01-Projects/
Скрипт поиска с анализом frontmatter
#!/usr/bin/env python3
"""
vault_search.py
Поиск по vault с учётом frontmatter.
Фильтрует по тегам, статусу, проекту.
"""
import os
import sys
import yaml
import argparse
from pathlib import Path
from datetime import datetime, date
VAULT_ROOT = Path(__file__).parent.parent
SKIP_DIRS = {'.obsidian', '.git', 'Templates'}
def parse_frontmatter(filepath):
content = filepath.read_text(encoding='utf-8', errors='ignore')
if not content.startswith('---'):
return {}, content
try:
end = content.index('---', 3)
fm_raw = content[3:end].strip()
return yaml.safe_load(fm_raw) or {}, content[end+3:]
except (ValueError, yaml.YAMLError):
return {}, content
def search_vault(query=None, tags=None, status=None, project=None,
modified_after=None):
"""Ищет заметки по критериям."""
results = []
for root, dirs, files in os.walk(VAULT_ROOT):
dirs[:] = [d for d in dirs if d not in SKIP_DIRS]
for fname in files:
if not fname.endswith('.md'):
continue
filepath = Path(root) / fname
fm, body = parse_frontmatter(filepath)
# Фильтр по статусу
if status and fm.get('status') != status:
continue
# Фильтр по проекту
if project and fm.get('project') != project:
continue
# Фильтр по тегам
if tags:
note_tags = fm.get('tags', [])
if isinstance(note_tags, str):
note_tags = [note_tags]
if not all(t in note_tags for t in tags):
continue
# Фильтр по дате изменения
if modified_after:
modified = fm.get('modified', fm.get('created'))
if modified:
if isinstance(modified, str):
try:
modified = date.fromisoformat(modified)
except ValueError:
pass
if isinstance(modified, date) and modified < modified_after:
continue
# Текстовый поиск
if query:
full_text = fname + ' ' + body
if query.lower() not in full_text.lower():
continue
rel_path = filepath.relative_to(VAULT_ROOT)
stat = filepath.stat()
results.append({
'path': str(rel_path),
'frontmatter': fm,
'modified': stat.st_mtime,
'size': stat.st_size
})
# Сортируем по дате изменения (свежие первыми)
results.sort(key=lambda x: x['modified'], reverse=True)
return results
def main():
parser = argparse.ArgumentParser(description='Поиск по vault')
parser.add_argument('query', nargs='?', help='Текстовый поиск')
parser.add_argument('--tag', action='append', dest='tags',
help='Фильтр по тегу (можно несколько)')
parser.add_argument('--status', help='Фильтр по статусу')
parser.add_argument('--project', help='Фильтр по проекту')
parser.add_argument('--after', help='Изменены после (YYYY-MM-DD)')
parser.add_argument('--limit', type=int, default=20,
help='Максимум результатов')
args = parser.parse_args()
after_date = None
if args.after:
after_date = date.fromisoformat(args.after)
results = search_vault(
query=args.query,
tags=args.tags,
status=args.status,
project=args.project,
modified_after=after_date
)
if not results:
print("Ничего не найдено")
sys.exit(0)
print(f"Найдено: {len(results)} (показываем {min(len(results), args.limit)})\n")
for r in results[:args.limit]:
fm = r['frontmatter']
tags = fm.get('tags', [])
status = fm.get('status', '-')
modified = datetime.fromtimestamp(r['modified']).strftime('%Y-%m-%d')
print(f" {r['path']}")
print(f" status={status} | tags={tags} | modified={modified}\n")
if __name__ == '__main__':
main()
# Поиск по тексту
python3 scripts/vault_search.py "JWT аутентификация"
# Все активные проекты
python3 scripts/vault_search.py --status active
# Все заметки с тегом debugging изменённые после 1 июня
python3 scripts/vault_search.py --tag debugging --after 2025-06-01
# Все заметки проекта my-app
python3 scripts/vault_search.py --project my-app
# Комбинация
python3 scripts/vault_search.py "docker" --tag backend --status in-progress
Добавь эти скрипты в CLAUDE.md с описанием когда какой использовать:
## Скрипты
- scripts/validate_frontmatter.py - проверка метаданных vault
- scripts/autotag.py --dry-run - предложить теги без изменений
- scripts/autotag.py - применить теги
- scripts/vault_search.py [запрос] [--tag X] [--status Y] - поиск
Кейс 3: MCP сервер — запросы к vault в реальном времени
Когда MCP настроен, Claude Code может обращаться к vault прямо в ходе работы — не читая все файлы целиком, а делая точечные запросы.
Примеры того что становится возможным:
# В диалоге с агентом:
"Что я уже знаю про Redis? Поищи в vault."
-> агент вызывает vault_search("redis"), читает топ-5 результатов,
синтезирует ответ из твоих же заметок
"Создай заметку по нашему сегодняшнему решению про JWT"
-> агент создаёт файл в _claude/decisions/ с правильным frontmatter,
добавляет ссылки на связанные заметки
"Какие у меня открытые задачи по проекту auth-refactor?"
-> агент ищет заметки с project: auth-refactor и status: in-progress,
выводит список TODO из их содержимого
"Найди все заметки с тегом debugging за последний месяц"
-> frontmatter-aware запрос, возвращает только релевантное
Пример реального диалога с настроенным MCP
> Что я решал по поводу архитектуры базы данных в последние 2 недели?
Claude Code: Нашёл 3 заметки в _claude/decisions/ за этот период:
1. decisions/2025-06-10-db-schema-v2.md
Решили перейти на UUID вместо integer ID.
Причина: масштабируемость при шардировании.
Статус: applied
2. decisions/2025-06-14-indexing-strategy.md
Составные индексы на (user_id, created_at) для timeline queries.
Бенчмарк показал x4 ускорение на 1M строк.
Статус: active
3. decisions/2025-06-18-connection-pooling.md
PgBouncer в transaction mode, pool size = CPU * 2.
Открыто: нужно проверить под нагрузкой > 500 concurrent connections.
Хочешь развернуть по любому из этих решений?
Хуки Claude Code: автозапись сессий в vault
Claude Code поддерживает хуки — скрипты которые запускаются автоматически при событиях: старт сессии, конец сессии, после каждого инструмента.
Stop хук — самый полезный. Запускается когда сессия завершается. Можно автоматически записывать итоги в vault.
Настройка Stop хука
# Создай файл настроек хуков
# macOS/Linux: ~/.claude/settings.json (глобально)
# или .claude/settings.json в папке проекта/vault (локально)
{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "python3 /Users/username/Documents/MyVault/scripts/session_capture.py"
}
]
}
]
}
}
Скрипт захвата сессии
#!/usr/bin/env python3
"""
session_capture.py
Stop хук Claude Code.
Читает stdin с данными сессии, пишет лог в vault.
"""
import sys
import json
import os
from pathlib import Path
from datetime import datetime
VAULT_PATH = Path.home() / "Documents" / "MyVault"
SESSIONS_DIR = VAULT_PATH / "_claude" / "sessions"
SESSIONS_DIR.mkdir(parents=True, exist_ok=True)
def main():
# Читаем JSON payload от Claude Code
try:
payload = json.loads(sys.stdin.read())
except (json.JSONDecodeError, ValueError):
sys.exit(0)
session_id = payload.get('session_id', 'unknown')
project_dir = payload.get('cwd', os.getcwd())
project_name = Path(project_dir).name
# Собираем инструменты которые использовались
tool_calls = payload.get('tool_calls', [])
files_touched = set()
for call in tool_calls:
tool = call.get('tool', '')
if tool in ('write_file', 'edit_file', 'create_file'):
path = call.get('input', {}).get('path', '')
if path:
files_touched.add(path)
now = datetime.now()
date_str = now.strftime('%Y-%m-%d')
time_str = now.strftime('%H-%M')
fname = f"{date_str}-{time_str}-{project_name}.md"
frontmatter = f"""---
date: {date_str}
time: {now.strftime('%H:%M')}
project: {project_name}
session_id: {session_id[:8]}
tags: [session-log, claude-code]
---
"""
body = f"""# Session Log: {project_name} {now.strftime('%Y-%m-%d %H:%M')}
## Проект
{project_dir}
## Файлы затронуты
"""
if files_touched:
for f in sorted(files_touched):
body += f"- {f}\n"
else:
body += "- (нет данных)\n"
body += """
## Итоги сессии
(заполни вручную или попроси агента)
## Открытые задачи
- [ ]
## Следующая сессия
"""
log_path = SESSIONS_DIR / fname
log_path.write_text(frontmatter + body, encoding='utf-8')
if __name__ == '__main__':
main()
Скрипт создаёт базовую структуру лога. Агент дополняет содержательную часть если попросить его в конце сессии написать итоги.
Поиск сломанных wiki-ссылок
Один из самых раздражающих артефактов большого vault — [[Ссылки на несуществующие заметки]]. Возникают когда файл переименовали или удалили. Находятся скриптом.
#!/usr/bin/env python3
"""
find_broken_links.py
Находит битые [[wiki-ссылки]] в vault.
"""
import os
import re
from pathlib import Path
VAULT_ROOT = Path(__file__).parent.parent
SKIP_DIRS = {'.obsidian', '.git', 'Templates'}
# Строим индекс всех заметок
def build_index():
index = {}
for root, dirs, files in os.walk(VAULT_ROOT):
dirs[:] = [d for d in dirs if d not in SKIP_DIRS]
for fname in files:
if fname.endswith('.md'):
# Имя без расширения - ключ для wiki-ссылок
name = Path(fname).stem.lower()
index[name] = Path(root) / fname
return index
def find_wikilinks(text):
"""Извлекает все [[ссылки]] из текста."""
pattern = r'\[\[([^\]|#]+?)(?:\|[^\]]+)?\]\]'
return re.findall(pattern, text)
def main():
index = build_index()
broken = []
for root, dirs, files in os.walk(VAULT_ROOT):
dirs[:] = [d for d in dirs if d not in SKIP_DIRS]
for fname in files:
if not fname.endswith('.md'):
continue
filepath = Path(root) / fname
content = filepath.read_text(encoding='utf-8', errors='ignore')
links = find_wikilinks(content)
for link in links:
# Нормализуем: убираем путь, берём только имя файла
link_name = Path(link).stem.lower()
if link_name not in index:
broken.append({
'source': str(filepath.relative_to(VAULT_ROOT)),
'broken_link': link
})
if not broken:
print("OK: битых ссылок не найдено")
return
print(f"Найдено битых ссылок: {len(broken)}\n")
for b in broken:
print(f" [{b['source']}]")
print(f" [[{b['broken_link']}]] - файл не существует\n")
if __name__ == '__main__':
main()
python3 scripts/find_broken_links.py
# Или агенту:
# "Запусти find_broken_links.py и исправь ссылки которые
# можно исправить автоматически (фузи-матч по имени файла)"
Типичные проблемы и решения
Claude Code не видит vault через MCP
Симптом: /mcp показывает пустой список или ошибку подключения.
# Проверь что Obsidian запущен (для вариантов A и B)
# Проверь что плагин включён
# Settings - Community plugins - убедись что плагин активен
# Для obsidian-claude-code-mcp: проверь порт
curl http://localhost:22360/health
# Должно вернуть JSON с ok: true
# Для mcp-obsidian: проверь Local REST API
curl http://localhost:27123/
# Должно вернуть ошибку 401 (не авторизован) - значит сервер работает
# Посмотри логи MCP
tail -f ~/Library/Logs/Claude/mcp-server-obsidian.log # macOS
# Windows: %APPDATA%\Claude\logs\
Агент пишет заметки без frontmatter
Причина: Правило не прописано явно в CLAUDE.md или прописано нечётко.
Решение: Добавь конкретный шаблон в CLAUDE.md:
## Шаблон новой заметки (ОБЯЗАТЕЛЬНО для каждого нового файла)
---
created: YYYY-MM-DD
modified: YYYY-MM-DD
tags: []
status: active
---
Конфликт Node.js версий при запуске MCP
# Проверь версию
node --version # нужно 18+
# Если старая - обнови через nvm
nvm install 20
nvm use 20
# Или через brew на macOS
brew upgrade node
Агент меняет заметки которые не должен трогать
Явно пропиши запреты в CLAUDE.md:
## Запрещено без явного запроса
- Изменять файлы в 04-Archive/
- Переименовывать существующие файлы
- Удалять теги из frontmatter
- Изменять поле created в frontmatter
Слишком много токенов тратится на обход vault
Причина: Vault большой, агент читает всё подряд.
Решение:
Создай _claude/hot.md — файл быстрого доступа. Обновляй его еженедельно. Это краткое резюме актуального состояния vault: текущие проекты, свежие решения, открытые задачи. Агент читает его первым, к полному vault обращается только если нужно.
# Hot Cache (обновлён: 2025-06-20)
## Активные проекты
- auth-refactor (01-Projects/auth-refactor/) - рефакторинг JWT, 80%
- api-v2 (01-Projects/api-v2/) - на паузе до согласования
## Последние решения
- UUID вместо int ID (_claude/decisions/2025-06-10-db-schema-v2.md)
- PgBouncer transaction mode (_claude/decisions/2025-06-18-connection-pooling.md)
## Открыто
- [ ] Тест нагрузки PgBouncer > 500 connections
- [ ] Миграция старых сессий в новую схему
## Ресурсы которые часто нужны
- 03-Resources/backend/postgres-tuning.md
- 03-Resources/backend/jwt-patterns.md
Профилактика: как не превратить vault в помойку
- Папка
_claude/— только для выходов агента. Твои мысли и заметки там не живут. Это разграничение исчезает удивительно быстро если его не блюсти. - Раз в неделю: запускай
validate_frontmatter.py. Дрейф метаданных происходит постепенно и незаметно, но накапливается. - CLAUDE.md должен меняться. Если ты работаешь с агентом месяц а CLAUDE.md такой же как в день создания — что-то не так.
.claudeignoreактуален. Добавляй туда всё что агент не должен индексировать: PDF, изображения, архив.- Git для vault — не опция, обязательство. Агент пишет в файлы. Нужна возможность откатиться.
Системные требования
| Компонент | Версия | Примечание |
|---|---|---|
| Claude Code | актуальная | npm install -g @anthropic-ai/claude-code |
| Node.js | 18+ | Для MCP серверов на JS |
| Python | 3.9+ | Для скриптов и mcp-obsidian |
| uv / pip | любая | Для mcp-obsidian через uvx |
| Obsidian | 1.4+ | Для MCP вариантов A и B |
| Git | 2.x | Для версионирования vault |
| ripgrep (rg) | любая | Опционально, для быстрого поиска |
На момент публикации актуальна версия Claude Code 1.x. Перед установкой проверь свежие релизы: docs.anthropic.com/en/docs/claude-code
Таблица портов
| Сервис | Протокол | Порт по умолчанию | Изменить |
|---|---|---|---|
| obsidian-claude-code-mcp WebSocket | WebSocket | 22360 | Настройки плагина |
| obsidian-claude-code-mcp HTTP/SSE | HTTP | 22361 | Настройки плагина |
| Obsidian Local REST API (HTTP) | HTTP | 27123 | Настройки плагина |
| Obsidian Local REST API (HTTPS) | HTTPS | 27124 | Настройки плагина |
Порты выше 1024, открывать в файрволе не нужно если только не работаешь удалённо.
FAQ
Нужен ли Obsidian открытым постоянно для работы с Claude Code?
Зависит от метода. Filesystem MCP работает без Obsidian — читает файлы напрямую с диска. Для obsidian-claude-code-mcp и mcp-obsidian (Local REST API) — Obsidian должен быть запущен. В режиме прямого запуска claude из папки vault — Obsidian вообще не нужен, агент работает с файлами напрямую.
Что написать в CLAUDE.md для первого запуска?
Минимум: структура папок vault и протокол сессии (что делать в начале и конце). Запусти claude из папки vault и попроси: «Просмотри структуру этого vault и сгенерируй стартовый CLAUDE.md». Агент проанализирует что есть и создаст разумный черновик. Потом дорабатывай.
Как Claude Code понимает wiki-ссылки [[вот такие]]?
Агент видит их как текст. Если ты описал в CLAUDE.md что [[ссылки]] — это внутренние ссылки на заметки по имени файла, он понимает логику и может переходить по ним через чтение нужного файла. Но автоматически по ним не переходит — только по явному запросу.
Можно ли использовать несколько vault одновременно?
Для obsidian-claude-code-mcp — каждый vault нужно запускать на отдельном порту. Для Filesystem MCP — добавь несколько путей в конфиг. Практически — удобнее держать один vault с хорошей структурой, чем несколько.
Claude Code пишет файлы в vault — это безопасно?
Относительно. Инициализируй vault как git репозиторий. Тогда любое изменение можно откатить. Запрети в CLAUDE.md изменение архивных файлов. И периодически запускай git diff чтобы видеть что агент наделал.
Как обновлять скрипты автоматизации?
Держи их в scripts/ внутри vault и коммить в git. Когда хочешь улучшить скрипт — просто скажи агенту что именно хочешь изменить. Он видит скрипт, редактирует, тестирует. Итоговая версия остаётся в файле.
Итог
Claude Code без памяти — мощный инструмент который каждый раз начинает с нуля. Obsidian vault как персистентный контекст превращает его в агента который знает твою историю, помнит решения и умеет работать с накопленными знаниями.
Базовый уровень — просто запусти claude из папки vault с хорошим CLAUDE.md. Работает сразу, ничего устанавливать не нужно. Добавь скрипты для фронтматтера и поиска — получишь инструмент который можно попросить «приведи в порядок теги в Inbox» и он справится без объяснений.
MCP сервер нужен когда vault вырос до 500+ заметок и нужен семантический поиск вместо grep. Или когда хочешь чтобы агент работал в коде и параллельно тянул контекст из vault не переключая директорию.
Хуки — финальный шаг. Vault начинает расти сам: каждая сессия оставляет структурированный след, агент следующей сессии читает его и продолжает с того места где остановился.
Оставайтесь на связи
Рецепты от IT-боли. Без воды, без рекламы, без маркетинговой шелухи.
Подписаться на IT-Аптеку →


