Skip to main content
Glama
sipho102
by sipho102

brain-mcp

Автор иконки: wenmeiZhou на iStock.

MCP-сервер, который предоставляет markdown-хранилище второго мозга, организованное по методу PARA (совместимое с Obsidian), в виде набора инструментов для чтения и одного ограниченного инструмента для записи (capture, который только создаёт новые заметки в 00-inbox/). Работает как Docker-контейнер, используется через streamable-HTTP клиентами Claude Code, opencode и другими MCP-клиентами.

Полная спецификация поведения: см. brain-mcp-spec.md в этом репозитории (или там, где вы его храните), если вам нужно «почему» за тем или иным проектным решением.

Примечания о парсере CONVENTIONS.md

brain_structure() читает перечисления type/status/domain напрямую из вашего файла 90-meta/CONVENTIONS.md, а не захардкоживает их (см. _extract_enum_values в src/brain_mcp/vault.py). Он был проверен на реальном файле хранилища: в этом файле три перечисления указаны как жирные встроенные метки под одним заголовком ## Enums (**type:** \note`, `project`, ...), а не каждое в отдельном подзаголовке, поэтому _extract_enum_values сначала пробует такую форму (ограничиваясь абзацем самой метки, чтобы не захватывать посторонние слова в кавычках-бэктиках в тексте ниже — например, «domain— основная ось запросов...»), а затем переходит к эвристике на основе заголовков для хранилищ, где перечисления описаны иначе. Если вы позже измените структуру раздела Enums вCONVENTIONS.md`, перепроверьте этот парсер — сервер громко падает при запуске, а не переходит к плохим значениям по умолчанию, если не может разобрать непустой список значений для всех трёх полей.

Формат uid: хранилище, на котором это проверялось, в настоящее время содержит противоречие в собственном CONVENTIONS.md — пример во frontmatter показывает UUIDv4, в тексте двумя строками ниже сказано, что реальный формат — YYYYMMDD-HHmm (+ буква при коллизиях в одну минуту), а реальные заметки на диске используют три разные схемы (UUIDv4 в одной заметке во входящих, 00000000-000N-заглушки в мета-документах, последовательные счётчики YYYYMMDD-000N в файлах _index.md). capture() генерирует UUIDv4 — это то, что просила исходная спецификация, соответствует текущему коду и сохраняет осмысленность поиска по короткому префиксу в read_note (≥8 символов), чего не было бы с низкоэнтропийным датированным id, где всё за один день имеет общий префикс. Если в вашем собственном CONVENTIONS.md описан другой формат uid, это стоит согласовать на стороне хранилища — сервер это автоматически не делает.

Related MCP server: Obsidian MCP Server

Требования

  • Структура PARA и схема frontmatter хранилища, как описано в собственном 90-meta/CONVENTIONS.md хранилища.

  • Docker (или Docker Compose) на Unraid-машине, или Python 3.12 + uv локально для разработки.

  • ripgrep в PATH (встроен в образ контейнера; установите отдельно для локальной разработки).

Конфигурация

Вся конфигурация через переменные окружения — ничего о конкретном хранилище (путь, имя, токен) не захардкожено, поэтому один и тот же образ обслуживает любое количество соседних хранилищ как отдельные контейнеры.

Переменная

Обязательная

По умолчанию

Значение

BRAIN_ROOT

да

—

Абсолютный путь к корню хранилища в контейнере

BRAIN_NAME

да

—

Имя экземпляра, например personal или family

BRAIN_TOKEN

да

—

Bearer-токен, обязательный для каждого MCP-запроса

PORT

нет

3100

Порт прослушивания

BIND_ADDRESS

нет

0.0.0.0

Адрес прослушивания

LOG_LEVEL

нет

INFO

DEBUG/INFO/WARNING/ERROR/CRITICAL

Скопируйте .env.example в .env и заполните BRAIN_NAME, BRAIN_VAULT_PATH (путь на хосте к вашему хранилищу) и BRAIN_TOKEN (случайный секрет — openssl rand -hex 32 подойдёт) перед запуском Compose.

Локальная разработка

uv sync --dev          # or: python3 -m venv .venv && .venv/bin/pip install -e ".[dev]"
uv run pytest          # or: .venv/bin/python -m pytest

Тесты полностью выполняются на синтетическом фикстурном хранилище, созданном в tests/conftest.py — никогда на реальных данных.

Чтобы запустить сервер локально на реальном (или тестовом) каталоге хранилища:

export BRAIN_ROOT=/path/to/vault
export BRAIN_NAME=personal
export BRAIN_TOKEN=dev-token
uv run brain-mcp

Запуск на Unraid

cp .env.example .env   # fill in BRAIN_NAME, BRAIN_VAULT_PATH, BRAIN_TOKEN
docker compose up -d --build   # or: docker compose pull && docker compose up -d
curl http://<unraid-host>:3100/health

--build собирает из этого репозитория; pull вместо этого загружает тот же образ, предварительно собранный из GHCR (см. «Установка как приложение Unraid» ниже) — оба варианта дают локальный тег ghcr.io/sipho102/brain-mcp:latest.

Файл compose монтирует BRAIN_VAULT_PATH в режиме только для чтения и поверх него монтирует только 00-inbox/ в режиме чтения-записи:

volumes:
  - ${BRAIN_VAULT_PATH}:/vault:ro
  - ${BRAIN_VAULT_PATH}/00-inbox:/vault/00-inbox:rw

Это намеренно и критически важно — даже ошибка в пути записи не может затронуть ничего за пределами входящих, независимо от того, что думает код Python. Не упрощайте это до единого монтирования чтения-записи.

Установка как приложение Unraid вместо этого

Если вы предпочитаете управлять этим через вкладку Docker в Unraid, как любым другим приложением — форма вместо редактирования .env, кнопки Start/Stop/Update после — для этого есть шаблон в unraid/brain-mcp.xml. .github/workflows/publish.yml собирает образ этого репозитория и публикует его в GHCR (ghcr.io/sipho102/brain-mcp:latest) при каждом push в main, и шаблон тянет его напрямую — без клонирования или сборки на Unraid-машине вообще.

Сделайте шаблон доступным для Unraid:

  • Рекомендуется — на вкладке Docker нажмите Add Container и вставьте URL сырого шаблона этого репозитория прямо в поле шаблона: https://raw.githubusercontent.com/sipho102/brain-mcp/main/unraid/brain-mcp.xml Так ничего не записывается в локальную папку шаблонов Unraid, поэтому не остаётся лишнего файла, который мог бы конфликтовать позже — см. предупреждение ниже об альтернативном методе.

  • Или скопируйте его в локальную папку шаблонов Unraid через SSH:

    curl -o /boot/config/plugins/dockerMan/templates-user/brain-mcp.xml \
      https://raw.githubusercontent.com/sipho102/brain-mcp/main/unraid/brain-mcp.xml

    Затем он появится в Docker → Add Container → выпадающем списке шаблонов — но см. примечание сразу после Add Container об удалении этого файла после создания контейнера.

В любом случае вы получите форму для пути к хранилищу, пути к входящим (должен быть <путь к хранилищу>/00-inbox — шаблон не может вывести его за вас), имени экземпляра и bearer-токена; всё остальное предзаполнено разумными значениями по умолчанию в «расширенном виде».

Если вы использовали метод локального копирования выше, удалите этот файл-затравку после добавления контейнера:

rm /boot/config/plugins/dockerMan/templates-user/brain-mcp.xml

Когда вы нажимаете Apply в Add Container, Unraid сохраняет второй файл с вашими реальными значениями — my-brain-mcp.xml, рядом с пустым, который вы скачали — и оба объявляют одно и то же имя контейнера. С двумя шаблонами, претендующими на это имя, Update может в итоге пересоздать контейнер из пустого оригинала вместо вашего сохранённого, стирая BRAIN_NAME/BRAIN_TOKEN/пути и оставляя его неработоспособным. Как только my-brain-mcp.xml существует (проверьте ls /boot/config/plugins/dockerMan/templates-user/), файл-затравка выполнил свою задачу и больше не нужен — удалите его, чтобы не было двусмысленности. После этого нажатие Update потянет самое свежее из ghcr.io/sipho102/brain-mcp:latest, используя вашу сохранённую конфигурацию, как и ожидается.

Обслуживание второго хранилища

Один контейнер обслуживает одно хранилище — намеренно нет списка мульти-хранилищных сервисов в docker-compose.yml, и нет мульти-хранилищной формы в шаблоне Unraid. На пути приложения Unraid это просто означает повторное Add Container из того же шаблона с другим именем/путями/токеном/портом. На пути Compose скопируйте этот каталог развёртывания (или просто docker-compose.yml + .env) в другое место, заполните в копии .env другие BRAIN_NAME, BRAIN_VAULT_PATH, BRAIN_TOKEN и PORT, и запустите docker compose up -d --build оттуда. Тот же образ (brain-mcp:latest), независимые контейнеры.

Пользователь контейнера / права доступа

Контейнер работает от непривилегированного пользователя, UID:GID 99:100 по умолчанию (Unraid nobody:users) — переопределите при сборке через BRAIN_UID/BRAIN_GID в .env, если вашему ресурсу нужны другие права владельца. Этот пользователь должен иметь доступ на запись к 00-inbox/ на хостовом ресурсе.

Подключение клиента

Claude Code

claude mcp add --transport http --scope user brain \
  http://<unraid-host>:3100/mcp \
  --header "Authorization: Bearer <token>"

Затем /mcp в сессии должен показать все шесть инструментов.

Известная особенность: у Claude Code были повторяющиеся ошибки, когда заголовки, заданные через --header, не отправлялись при установлении сессии, что приводило к 401, хотя curl с тем же токеном работал. Если вы столкнулись с этим, запишите объект headers напрямую в JSON-конфиг (~/.claude/mcp_servers.json или соответствующий файл области):

{
  "mcpServers": {
    "brain": {
      "type": "http",
      "url": "http://<unraid-host>:3100/mcp",
      "headers": {
        "Authorization": "Bearer <token>"
      }
    }
  }
}

(type также принимает streamable-http как псевдоним для http в JSON-конфигах.)

opencode

opencode по умолчанию пытается выполнить OAuth-обнаружение на удалённых MCP-серверах и игнорирует статический bearer-токен, если вы явно не отключите это:

{
  "mcp": {
    "brain": {
      "type": "remote",
      "url": "http://<unraid-host>:3100/mcp",
      "oauth": false,
      "headers": {
        "Authorization": "Bearer <token>"
      }
    }
  }
}

Без oauth: false opencode попытается выполнить (и провалит) OAuth-рукопожатие вместо использования заголовка.

Клиенты без поля заголовка вообще

Некоторые интерфейсы MCP-клиентов принимают только имя, транспорт и URL — нет способа задать пользовательский заголовок Authorization. Для таких поместите токен в URL:

http://<unraid-host>:3100/mcp?token=<token>

Сервер сначала проверяет заголовок Authorization, а затем переходит к параметру запроса ?token=, так что это работает везде, где работает и конфигурация с заголовком выше. Стоит знать, прежде чем полагаться на это: токен в URL может оказаться в большем количестве мест, чем заголовок — в сохранённой конфигурации клиента, в истории браузера, если URL когда-либо открыт напрямую, в истории оболочки, если вы вставили его в терминал. Журналы доступа здесь не проблема (журнал доступа uvicorn выключен), но относитесь к самому URL как к носителю секрета, так же, как и к самому токену.

Инструменты

Шесть инструментов, намеренно небольших (схемы инструментов стоят контекста клиента):

  • brain_structure() — ориентация: папки PARA + счётчики, живые перечисления из CONVENTIONS.md, схема frontmatter, полный текст соглашений, количество заметок. Вызывайте это первым в сессии.

  • search_notes(query, domain, type, status, para, tag, limit) — полнотекстовый поиск (ripgrep) с фильтрацией по frontmatter. Возвращает метаданные + фрагмент ~200 символов, никогда полные тела.

  • read_note(identifier) — полная заметка по пути относительно хранилища, полному uid или однозначному префиксу uid (≥8 символов).

  • list_notes(para, domain, status, type, limit) — просмотр только метаданных, без поиска по содержимому.

  • get_backlinks(identifier) — заметки, ссылающиеся на эту, с контекстной строкой.

  • capture(title, body, domain, tags, source, links) — единственная запись: создаёт новую заметку в 00-inbox/. Никогда не перезаписывает, никогда не касается ничего за пределами входящих.

Что это намеренно не делает

Нет семантического поиска/эмбеддингов, нет доступа на запись за пределами 00-inbox/, нет зависимости от Obsidian Local REST API (читает файловую систему напрямую), нет получения документов paperless-ngx (возвращает ID документов из frontmatter, чтобы клиент мог связать их с отдельным MCP-сервером paperless), нет операций с git. См. brain-mcp-spec.md §2 для обоснования.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    A
    maintenance
    A generic Markdown vault MCP server with FTS5 full-text search, semantic vector search, frontmatter-aware indexing, incremental reindexing, and non-markdown attachment support that exposes search, read, write, and edit tools.
    44
    230 PyPI
    35
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Exposes an Obsidian notes vault as MCP services, enabling AI assistants to search, read, create, update, and delete notes and folders.
    12 npm
    1
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Exposes a personal markdown-based second brain (Obsidian-style) as an MCP server, enabling agents to search, read, and write notes with privacy controls.
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    Enables MCP clients to read, write, search, and manage notes, canvases, kanban boards, and other Obsidian vault content over a network, without requiring the Obsidian desktop app.
    33
    MIT