Skip to main content
Glama
thecodacus

OKF Knowledge Agent MCP Server

by thecodacus

understory 🌱

不断生长的记忆。

位于你的智能体之下的一层:一种自连线的纯 Markdown 记忆。你的智能体学到的每一条事实都会被整理为一个 Markdown 概念,交叉链接进一个活的知识图谱,并由智能体自身维护其健康——可搜索、可 diff、完全归你所有。在本地模型上运行效果很好。

Bundle 遵循 Open Knowledge Format (OKF) v0.1 spec —— 带 YAML frontmatter 的纯 Markdown 文件,人类可读、可在 git 中 diff、可跨工具移植。

三种接入方式,同一个智能体:

  • MCP server —— 通过 stdio 或 streamable HTTP 提供 memory_query / memory_add / memory_update / memory_status / memory_maintain 工具。每次调用都会驱动一个内部 LLM 智能体,其系统提示词中包含 OKF 规范。

  • Web UI —— 浏览 bundle(目录树、概念查看器、更新日志、一致性徽章),以 Obsidian 风格的力导向图查看记忆(拖拽/平移/缩放,按类型着色,按连接数决定大小,孤立概念以红色圆环标出,点击打开),并与同一个智能体聊天来测试它。工具调用会内联渲染,让你能观察它的工作过程。

  • 查询路径回放 —— 每次智能体运行(查询/变更/聊天)都会将其遍历过程(搜索 → 读取 → 写入)以紧凑记法记录下来,持久化在 <bundle>/.traces/ 下。图视图会列出最近的运行;选中一次运行后,会以编号的有向跳转在图上回放路径——已访问的概念带圆环,搜索命中用点线标出,其余全部淡化。

  • CLI —— pnpm agent:query "..." / pnpm agent:mutate "..." 冒烟测试入口。

设计原则:一致性由代码强制保证,而非提示词。 确定性的 bundle 层会校验 frontmatter(type 必填)、重新生成 index.md 文件、追加 log.md 条目(最新的在前,规范 §7),并将所有路径沙箱化到 bundle 根目录。LLM 决定要改什么;代码保证结果是一个符合规范的 bundle。

快速开始(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

选择提供商

通用提供商系统支持任何兼容 OpenAI 或兼容 Anthropic 的 API。 设置 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-chat

OpenAI:

LLM_API_BASE_URL=https://api.openai.com/v1 LLM_API_KEY=sk-... LLM_MODEL=gpt-4o

Anthropic (Claude):

LLM_API_BASE_URL=https://api.anthropic.com/v1 LLM_API_KEY=sk-ant-... LLM_API_FORMAT=anthropic LLM_MODEL=claude-sonnet-5

Groq:

LLM_API_BASE_URL=https://api.groq.com/openai/v1 LLM_API_KEY=gsk_... LLM_MODEL=llama-3.3-70b-versatile

本地 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。

本地 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 以及各提供商专属的密钥环境变量仍然有效(向后兼容),但已弃用。

然后:

  • Web UI → 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 monorepo:

Package

说明

packages/core

OKF bundle 层(零 LLM)+ 智能体(Vercel AI SDK 工具循环:search/read/list/write/patch/delete)+ 提供商注册表

packages/server

Express:MCP streamable-HTTP 位于 /mcp,stdio 二进制,REST 浏览 API 位于 /api/*,流式聊天位于 /api/chat,托管 Web 构建产物

packages/web

Vite + React + TS + Tailwind:bundle 浏览器 + 智能体聊天(useChat)

提供商通过 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 或可信 LAN 上没问题。在将其暴露到任何其他地方之前,请设置 AUTH_TOKEN:

AUTH_TOKEN=$(openssl rand -hex 24)

设置后,/mcp 和 /api 需要 Authorization: Bearer <token>(Web UI 仍然可访问,并会提示输入 token)。使用请求头注册已认证的 MCP 客户端:

claude mcp add --transport http ustory http://host:3800/mcp \
  --header "Authorization: Bearer <token>"

stdio 传输不需要 token——它是由客户端生成的本地进程。

种子记忆

一个只看到四个光秃秃的工具名称的客户端 LLM 永远不会产生检查记忆的本能。因此,在会话开始时,服务器会通过两条到达模型的通道注入知识库内容的紧凑概览(目录、带类型和描述的概念、近期活动):

  1. MCP initialize 的 instructions 字段(像 Claude 这样的客户端会将其放入系统提示词),以及

  2. memory_query 工具描述 —— 每个工具调用客户端都会加载的通用回退。

种子会在每个新会话中重新生成。在长生命周期(stdio)会话中执行 memory_add / memory_update 后,工具描述会通过 tools/list_changed 刷新,因此会话能看到自己的写入。带外编辑(手动编辑、其他客户端)会在下一个会话中被捕获。

图谱健康与维护

记忆是一个图谱,而不是一堆笔记,而图谱会腐烂:概念会变成孤立(没有任何链接指向它们),链接会断裂。有两种机制保持其健康:

  • 写入时链接 —— 新知识要么丰富它所属的概念(现有实体的属性被修补进去,而不是单独归档),要么在它是一个独立实体时被创建并从相关概念反向链接。矛盾会在原地被取代,绝不会与旧值并存。

  • memory_maintain —— 一个确定性的 lint(孤立概念 + 断裂链接,在 memory_status 的 graph 下显示)驱动内部智能体将孤立概念接入相关概念并修复悬空链接。定期运行它以对抗漂移;当图谱已经健康时,它是一个空操作。

这一设计借鉴了 Karpathy 的 LLM Wiki 中的模式(index.md + log.md、创建与丰富之争、针对孤立的 lint)。在规模需要之前,暂缓实现该模式中的:显式页面类型 schema,以及混合 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 会为每次变更提交。

Maintenance

ActivityMaintained
ResponsivenessSlow

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    Not graded
    maintenance
    A Knowledge Graph MCP server optimized for LLM context efficiency through compact JSON and SQLite persistence. It enables full graph management including node/edge CRUD operations, full-text search, and subgraph traversal.
    -
  • A
    license
    A
    quality
    A
    maintenance
    MCP 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).
    10
    15
    Apache 2.0
  • F
    license
    Not graded
    quality
    D
    maintenance
    MCP server exposing a RAG knowledge base as read-only tools (search_knowledge_base, ask_knowledge_base, kb_stats, kb_diagnostic) for AI clients like Claude Desktop and Cursor, enabling token-efficient document retrieval and Q&A.
    -