OKF Knowledge Agent MCP Server
understory 🌱
Память, которая растёт.
Слой под вашими агентами: самоорганизующаяся память на основе простого Markdown. Каждый факт, который узнают ваши агенты, сохраняется как markdown-концепция, перекрёстно связывается в живой граф знаний и поддерживается в здоровом состоянии самим агентом — доступная для поиска, диффабельная и полностью ваша. Отлично работает на локальных моделях.
Бандлы соответствуют спецификации Open Knowledge Format (OKF) v0.1 — обычные markdown-файлы с YAML-frontmatter, читаемые людьми, диффабельные в git, переносимые между инструментами.
Три способа доступа — один агент:
MCP-сервер — инструменты
memory_query/memory_add/memory_update/memory_status/memory_maintainчерез stdio или streamable HTTP. Каждый вызов управляет внутренним LLM-агентом со спецификацией OKF в системном промпте.Веб-интерфейс — просматривайте бандл (дерево, просмотрщик концепций, журнал обновлений, значок соответствия), видите память как силовой граф в стиле Obsidian (перетаскивание/панорамирование/масштабирование, цвет по типу, размер по связям, осиротевшие обведены красным, клик открывает) и общайтесь с тем же агентом, чтобы протестировать его. Вызовы инструментов отображаются инлайн, так что вы можете наблюдать за работой.
Воспроизведение пути запроса — каждый запуск агента (запрос/мутация/чат) записывает свой обход (поиски → чтения → записи) в компактной нотации, сохраняемой в
<bundle>/.traces/. В представлении графа перечислены недавние запуски; выбор одного воспроизводит путь в виде нумерованных направленных переходов по графу — посещённые концепции обведены кольцом, результаты поиска пунктиром, всё остальное затемнено.CLI — проверочные команды
pnpm agent:query "..."/pnpm agent:mutate "...".
Правило проектирования: соответствие обеспечивается кодом, а не промптами. Детерминированный слой бандла проверяет frontmatter (обязателен type), перегенерирует файлы index.md, добавляет записи в log.md (сначала новые, спецификация §7) и изолирует все пути в корне бандла. LLM решает, что изменить; код гарантирует, что результат — соответствующий спецификации бандл.
Быстрый старт (Docker)
Клонировать не нужно — образ публичный. Сохраните это как docker-compose.yml:
services:
understory:
image: ghcr.io/thecodacus/understory:latest
ports:
- "3800:3800"
# Lets the container reach a llama.cpp server running on the host via
# http://host.docker.internal:8080/v1 (see "Local llama.cpp" below).
extra_hosts:
- "host.docker.internal:host-gateway"
volumes:
# Your memory lives here as plain markdown — a named volume, or point
# a bind mount (e.g. ./my-memory:/bundle) at any OKF bundle.
- understory-memory:/bundle
environment:
BUNDLE_ROOT: /bundle
LLM_API_BASE_URL: ${LLM_API_BASE_URL}
LLM_API_KEY: ${LLM_API_KEY}
LLM_API_FORMAT: openai
LLM_MODEL: ${LLM_MODEL:-}
# Optional fallback
LLM_FALLBACK_API_BASE_URL: ${LLM_FALLBACK_API_BASE_URL:-}
LLM_FALLBACK_API_KEY: ${LLM_FALLBACK_API_KEY:-}
LLM_FALLBACK_API_FORMAT: ${LLM_FALLBACK_API_FORMAT:-openai}
LLM_FALLBACK_MODEL: ${LLM_FALLBACK_MODEL:-}
restart: unless-stopped
volumes:
understory-memory:docker compose up -dВыбор провайдера
Универсальная система провайдеров поддерживает любой API, совместимый с OpenAI или Anthropic. Задайте LLM_API_BASE_URL + LLM_API_KEY + LLM_MODEL и оставьте LLM_PROVIDER незаданным.
DeepSeek:
LLM_API_BASE_URL=https://api.deepseek.com/v1 LLM_API_KEY=sk-... LLM_MODEL=deepseek-chatOpenAI:
LLM_API_BASE_URL=https://api.openai.com/v1 LLM_API_KEY=sk-... LLM_MODEL=gpt-4oAnthropic (Claude):
LLM_API_BASE_URL=https://api.anthropic.com/v1 LLM_API_KEY=sk-ant-... LLM_API_FORMAT=anthropic LLM_MODEL=claude-sonnet-5Groq:
LLM_API_BASE_URL=https://api.groq.com/openai/v1 LLM_API_KEY=gsk_... LLM_MODEL=llama-3.3-70b-versatileLocal llama.cpp:
LLM_API_BASE_URL=http://host.docker.internal:8080/v1 LLM_MODEL=Когда understory работает в Docker,
localhost— это сам контейнер, а не хост, поэтому llama-server на хосте доступен по адресуhost.docker.internal(файлы compose выше уже пробрасывают его черезextra_hosts). При запуске из исходников на той же машине, где работает llama-server, используйтеhttp://localhost:8080/v1.
Local llama.cpp с запасным вариантом DeepSeek:
LLM_API_BASE_URL=http://host.docker.internal:8080/v1 LLM_MODEL= \
LLM_FALLBACK_API_BASE_URL=https://api.deepseek.com/v1 LLM_FALLBACK_API_KEY=sk-... LLM_FALLBACK_MODEL=deepseek-chatСтарые переменные окружения LLM_PROVIDER + ключи для каждого провайдера по-прежнему работают (обратная совместимость), но устарели.
Затем:
Веб-интерфейс → http://localhost:3800 — просматривайте память, наблюдайте за графом, общайтесь с агентом
MCP-эндпоинт →
http://localhost:3800/mcp(streamable HTTP) — зарегистрируйте его в любом MCP-клиенте:claude mcp add --transport http ustory http://localhost:3800/mcpТеперь ваш агент имеет
memory_query/memory_add/memory_update/memory_status/memory_maintainи получает сид-обзор памяти при каждом старте сессии.
Научите его чему-нибудь (memory_add: «Мы выкатываемся по пятницам, никогда по понедельникам»), затем откройте граф и наблюдайте, как концепция встраивается сама. Разворачиваете через Portainer? Используйте docker-compose.portainer.yml как стек репозитория.
Related MCP server: Kremis
Стек
pnpm монорепозиторий:
Package | Что |
| Слой бандла OKF (без LLM) + агент (цикл инструментов Vercel AI SDK: search/read/list/write/patch/delete) + реестр провайдеров |
| Express: MCP streamable-HTTP на |
| Vite + React + TS + Tailwind: браузер бандла + чат с агентом ( |
Провайдеры настраиваются через LLM_API_BASE_URL, LLM_API_KEY, LLM_API_FORMAT (openai или anthropic) и LLM_MODEL. Любой OpenAI-совместимый эндпоинт (DeepSeek, OpenAI, Groq, OpenRouter, llama.cpp и т.д.) работает с LLM_API_FORMAT=openai; Anthropic-совместимые эндпоинты используют LLM_API_FORMAT=anthropic. Опциональный запасной вариант использует соответствующие переменные LLM_FALLBACK_*.
llama.cpp
# on the inference box — --jinja enables OpenAI-style tool calling
llama-server -m model.gguf --jinja --host 0.0.0.0 --port 8080
# here — no model id needed, it's discovered for llama-server-like local endpoints
LLM_API_BASE_URL=http://inference-box:8080/v1 LLM_API_FORMAT=openai LLM_MODEL= \
BUNDLE_ROOT=./sample-bundle node packages/server/dist/index.jsТакже работает за llama-swap: при обнаружении предпочитается текущая загруженная модель, чтобы запрос не вызывал многоминутную смену модели. Зафиксируйте конкретную модель с помощью LLM_MODEL=.
Из исходников
pnpm install
pnpm build
cp .env.example .env # add your API key
BUNDLE_ROOT=./sample-bundle \
LLM_API_BASE_URL=https://api.deepseek.com/v1 \
LLM_API_KEY=sk-... \
LLM_API_FORMAT=openai \
LLM_MODEL=deepseek-chat \
node packages/server/dist/index.js
# → http://localhost:3800 (web UI + /api + /mcp)Или соберите контейнер сами: docker compose up --build (docker-compose.yml из репозитория собирает из исходников и монтирует ./sample-bundle).
Режим разработки (сервер на :3800, Vite HMR на :5180 с прокси):
BUNDLE_ROOT=./sample-bundle pnpm --filter @understory/server dev
pnpm --filter @understory/web devРегистрация MCP (Claude Code / Desktop)
claude mcp add ustory \
-e BUNDLE_ROOT=/path/to/your/bundle \
-e LLM_API_BASE_URL=https://api.deepseek.com/v1 \
-e LLM_API_KEY=sk-... \
-e LLM_API_FORMAT=openai \
-e LLM_MODEL=deepseek-chat \
-- node /path/to/understory/packages/server/dist/mcp/stdio.jsИли укажите HTTP MCP-клиенту адрес http://host:3800/mcp.
Аутентификация
По умолчанию сервер открыт — это нормально для localhost или доверенной локальной сети. Прежде чем открывать его где-либо ещё, задайте AUTH_TOKEN:
AUTH_TOKEN=$(openssl rand -hex 24)Когда он задан, /mcp и /api требуют Authorization: Bearer <token> (веб-интерфейс остаётся доступным и запрашивает токен). Регистрируйте аутентифицированные MCP-клиенты с заголовком:
claude mcp add --transport http ustory http://host:3800/mcp \
--header "Authorization: Bearer <token>"Транспорт stdio не требует токена — это локальный процесс, запускаемый клиентом.
Сид памяти
Клиентская LLM, которая видит только четыре голых имени инструментов, никогда не получит инстинкт проверять память. Поэтому при старте сессии сервер внедряет компактный обзор того, что содержит база знаний (каталоги, концепции с типами + описаниями, недавняя активность), через оба канала, которые достигают модели:
поле
instructionsинициализации MCP (такие клиенты, как Claude, помещают его в системный промпт), иописание инструмента
memory_query— универсальный запасной вариант, который загружает каждый клиент, вызывающий инструменты.
Сид перегенерируется заново для каждой новой сессии. После memory_add / memory_update в долгоживущей (stdio) сессии описание инструмента обновляется через tools/list_changed, так что сессия видит свои собственные записи. Внеполосные изменения (ручные правки, другие клиенты) подхватываются в следующей сессии.
Здоровье графа и обслуживание
Память — это граф, а не куча заметок, а графы портятся: концепции становятся осиротевшими (ничто на них не ссылается), а ссылки — битыми. Два механизма поддерживают его в порядке:
Связывание при записи — новое знание либо обогащает концепцию, к которой оно относится (атрибут существующей сущности вносится патчем, а не заводится отдельно), либо, если это отдельная сущность, создаётся и связывается обратными ссылками из связанных концепций. Противоречия замещаются на месте и никогда не остаются рядом со старым значением.
memory_maintain— детерминированный линт (осиротевшие + битые ссылки, отображаемые вmemory_statusв разделеgraph) управляет внутренним агентом, чтобы встроить осиротевшие концепции в связанные и исправить висячие ссылки. Запускайте его периодически для противодействия дрейфу; когда граф уже здоров, он ничего не делает.
Эта конструкция повторяет паттерн из LLM Wiki Карпати (index.md + log.md, create-vs-enrich, линт для осиротевших). Отложено из этого паттерна до тех пор, пока масштаб не потребует: явная схема типов страниц и гибридный поиск FTS5+embedding (наивный скан в search.ts нормально работает до нескольких тысяч концепций).
Тесты
pnpm test # core: 18 tests (spec §5/§6/§7/§9, sandbox, search, concurrency)
pnpm --filter @understory/server exec tsx scripts/mcp-smoke.mts # MCP stdio round-trip (needs SMOKE_BUNDLE + an API key)Окружение
Смотрите .env.example. BUNDLE_ROOT обязателен; GIT_AUTOCOMMIT=true коммитит каждую мутацию.
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 Connectors
Knowledge base MCP for AI agents on iknow.dev. Search, read, and maintain via OAuth.
AI Reasoning Cache & Consensus Layer with 11 MCP tools via Streamable HTTP.
Shared, governed long-term memory for AI agents across tools and sessions via MCP and REST.
Private-by-default, local-first memory/context/task orchestrator for MCP apps and agents.
Related MCP Servers
- AlicenseAqualityAmaintenanceMCP server exposing a deterministic, local knowledge graph over stdio. Zero LLM calls in the bridge; answers are classified as Fact, Inference, or Unknown and persisted in redb (ACID, BLAKE3-hashed).1014Apache 2.0
- AlicenseNot gradedqualityBmaintenanceA local OKF-compatible knowledge engine for AI agents. Enables capturing agent conversations, hybrid semantic+keyword search, MCP serving to agents, interactive graph visualization, and OKF bundle export.Apache 2.0
- AlicenseNot gradedqualityBmaintenanceProvides LLM agents with a structured, queryable, local-first knowledge base with typed documents and full-text search via MCP.MIT
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/thecodacus/understory'
If you have feedback or need assistance with the MCP directory API, please join our Discord server