Skip to main content
Glama
arptra

local-corporate-kb

by arptra

Локальная корпоративная база знаний для Qwen Code

Это локальный MVP корпоративного RAG: документы индексируются Python-процессом, embeddings сохраняются в проверяемый файловый кэш, а при поиске целиком находятся в RAM. Qwen Code остаётся единственной генеративной моделью и получает найденные фрагменты через read-only MCP tools по локальному stdio или удалённому Streamable HTTP. MCP-сервер не формулирует финальные ответы, не исполняет shell-команды и не изменяет документы.

Проект рассчитан на Python 3.12 и standalone FastMCP==3.4.4. Запуск после установки не зависит от uv: все runtime-скрипты вызывают Python из .venv напрямую. Для разработки доступна воспроизводимая установка через uv.lock, а для корпоративных машин — отдельная установка через обычный pip.

Отдельные пошаговые инструкции:

Архитектура

Confluence export
       ↓
knowledge/*.md, *.html, *.txt
       ↓
loader + normalizer + structural chunker
       ↓
local feature hashing (по умолчанию) или локальная embedding-модель
       ↓
NumPy matrix in RAM
       ↓
MCP stdio / Streamable HTTP
       ↓
Qwen Code CLI

В удалённом режиме тот же индекс один раз загружается в память серверного процесса, после чего к нему одновременно подключаются Qwen Code CLI с разных машин:

Qwen CLI ─┐
Qwen CLI ─┼─ HTTPS / Bearer token ─ MCP Streamable HTTP ─ in-memory index
Qwen CLI ─┘

DocumentLoader безопасно обходит только KB_KNOWLEDGE_DIR, нормализует Markdown/TXT и переводит экспортированный HTML в Markdown-подобный текст. StructuralChunker сохраняет путь заголовков, списки, таблицы и code fences. KnowledgeService координирует кэш и работает только через интерфейс KnowledgeStore; MCP-слой не знает о NumPy.

В RAM находятся документы, чанки, отображение chunk_id -> index и нормализованная NumPy-матрица [chunk_count, embedding_dimension]. Cosine similarity считается как matrix @ query_vector.

Экономия контекста Qwen

Поиск не передаёт модели все найденные тексты. Сервер сначала находит до 12 кандидатов внутри индекса, затем отдаёт максимум 3 наиболее релевантные выдержки из разных документов: до 260 условных токенов на выдержку и до 1000 на один ответ инструмента. Выдержка выбирается по словам вопроса и сохраняет ссылку на источник. Это ограничивает расход контекста, даже если в базе десятки тысяч страниц.

Если выбранный результат требует деталей, Qwen вызывает kb_get_chunk с chunk_id; полный текст не загружается автоматически. kb_get_document также возвращает ограниченный извлекаемый фрагмент. Лимиты настраиваются через KB_SEARCH_* и KB_DOCUMENT_CONTEXT_TOKENS в .env.example.

Защищённый kb_run_context_benchmark сравнивает прежние top-5 полных чанков с текущими top-3 выдержками: Hit@K, оценку токенов, точные JSON bytes, процент сжатия и latency. Инструмент требует отдельный KB_BENCHMARK_PASSWORD, который Qwen запрашивает перед каждым вызовом.

В HTTP-режиме по /admin доступна встроенная парольная панель: usage-счётчики, живая системная нагрузка, память и uptime процесса, список документов, загрузка Markdown/HTML/TXT и фоновая инкрементальная переиндексация. Декларативные MCP search-tools по-прежнему доступны через защищённый API, но технический редактор schemas из основной панели убран.

На диске в .cache/kb/ находятся только:

  • manifest.json — версии схемы, идентичность модели, chunking config и knowledge hash;

  • documents.json — нормализованные документы и metadata;

  • chunks.json — чанки без отдельной копии embedding;

  • embeddings.npy — матрица без pickle.

Это не Vector DB: нет отдельного сервиса хранения, индекса ANN или SQL. Удалённый HTTP — только read-only MCP-фасад; поиск выполняется полным cosine scan по NumPy-матрице в памяти, а диск используется для ускорения старта.

Related MCP server: sage-mcp

Первый запуск

Подключение сотрудника к удалённой базе

RAG, индекс и документы находятся только на сервере. Для старых версий Qwen сотруднику передаётся один файл clients/corporate_kb_stdio_proxy.py. Qwen запускает его локально через uv как stdio MCP, а Python-процесс ходит к удалённому серверу через обычные HTTP GET-запросы. Node.js, npx, mcp-remote, Nginx, копия проекта, .venv, документы и индекс на клиенте не нужны.

Готовый settings находится в examples/qwen-uv-stdio-settings.example.json, а полная инструкция для сотрудника — в README.client.md.

Новые версии Qwen также могут подключаться к /mcp напряму через Streamable HTTP; скрипт install.sh оставлен как опциональный способ для таких клиентов. Скопируйте из неё mcpServers в ~/.qwen/settings.json и замените четыре placeholder:

  • REPLACE_WITH_ABSOLUTE_UV_PATH — результат which uv или where uv (в Windows uv.exe);

  • REPLACE_WITH_ABSOLUTE_PATH — каталог, в котором сотрудник сохранил единственный .py-файл;

  • REPLACE_WITH_SERVER_IP_OR_DOMAIN — адрес удалённого сервера;

  • REPLACE_WITH_SERVER_TOKEN — Bearer-токен.

Qwen запускает этот файл как локальный MCP по stdio командой uv run. Скрипт содержит inline dependency на FastMCP, поэтому uv сам создаёт изолированное кэшированное окружение. Локальный MCP обращается к удалённому RAG только через обычные авторизованные JSON GET endpoints /api/v1/*. node, npx, mcp-remote, локальная копия документов и локальный индекс не нужны.

Установка серверной части

Убедитесь, что доступен Python 3.12. На корпоративной машине рекомендуется pip-вариант: он не читает uv.lock, не запускает uv и не зависит от установленной в системе версии uv. Hugging Face, PyTorch и sentence-transformers в базовую установку не входят:

./scripts/setup-pip.sh
source ./scripts/activate-venv.sh

Для разработки с точными версиями из lock-файла остаётся вариант:

./scripts/setup-venv.sh
source ./scripts/activate-venv.sh

Оба варианта создают одинаковую .venv; runtime-команды используют только .venv/bin/python.

Полностью локальный режим по умолчанию использует hash provider и не требует модели или сети:

./scripts/dev.sh index-hash
./scripts/dev.sh search-hash

Hash provider строит локальные lexical vectors из слов и символьных триграмм. Он пригоден для полностью автономного поиска по совпадающей терминологии, но не понимает смысл и синонимы так же хорошо, как semantic embedding model.

Для качественного semantic search сначала положите заранее полученные и одобренные model files в локальный каталог. Этот проект не скачивает их. Например:

models/Qwen3-Embedding-0.6B/

После этого активируйте окружение, укажите только локальный путь и постройте индекс:

./scripts/dev.sh install-pip-semantic
source ./scripts/activate-venv.sh
export KB_EMBEDDING_PROVIDER=sentence_transformers
export KB_EMBEDDING_MODEL="$KB_PROJECT_ROOT/models/Qwen3-Embedding-0.6B"
export KB_EMBEDDING_LOCAL_FILES_ONLY=true

./scripts/dev.sh index-semantic
./scripts/dev.sh search "Какой сервис владеет дневными лимитами?"

local_files_only=true, HF_HUB_OFFLINE=1 и TRANSFORMERS_OFFLINE=1 запрещают обращения к Hugging Face. Если model files отсутствуют, индексирование завершится понятной ошибкой без попытки скачивания. По умолчанию выбирается CUDA, затем MPS, затем CPU.

scripts/start-mcp.sh по умолчанию запускает MCP с KB_EMBEDDING_PROVIDER=hash, поэтому обычное подключение Qwen полностью offline. Для локальной semantic-модели явно передайте provider и путь в environment Qwen-конфигурации. Все runtime wrappers вызывают Python из готовой .venv напрямую: после установки они не обращаются к package registry и не меняют окружение.

CLI

./.venv/bin/python -m corporate_kb.cli index
./.venv/bin/python -m corporate_kb.cli index --force
./.venv/bin/python -m corporate_kb.cli search "Как рассчитывается дневной лимит?" --top-k 5
./.venv/bin/python -m corporate_kb.cli search "Как рассчитывается дневной лимит?" --service limits-service
./.venv/bin/python -m corporate_kb.cli documents
./.venv/bin/python -m corporate_kb.cli stats
./.venv/bin/python -m corporate_kb.cli eval --top-k 5

У search, documents, stats и eval есть --json. В этом режиме stdout содержит только JSON, а логи остаются в stderr.

Если кэша нет или он несовместим, обычный поиск при KB_AUTO_INDEX=false завершится практичным сообщением Run: ./scripts/dev.sh index. Это предотвращает неожиданную сетевую активность во время MCP discovery.

Подключение к Qwen Code

Если MCP-серверы хранятся в отдельном каталоге, установите туда автономную runtime-копию. Скрипт создаёт подкаталог corporate-kb, копирует только необходимые файлы, создаёт собственный .venv, ставит locked runtime dependencies без dev-пакетов, строит hash-индекс и печатает готовый server entry для Qwen:

./scripts/install-mcp-server.sh /absolute/path/to/mcp-servers

Если версия uv на целевой машине отличается или uv запрещён политиками, установите ту же runtime-копию через pip:

./scripts/install-mcp-server.sh /absolute/path/to/mcp-servers --pip

Для закрытого окружения можно сначала только скопировать файлы, затем настроить корпоративный Python package registry и завершить установку командами, которые напечатает скрипт:

./scripts/install-mcp-server.sh /absolute/path/to/mcp-servers --copy-only

Скопируйте examples/qwen-settings.example.json в .qwen/settings.json проекта и замените все /ABSOLUTE/PATH/... реальными абсолютными путями. Не рассчитывайте на раскрытие ${PROJECT_ROOT} в JSON. В command указан абсолютный путь к .venv/bin/python, а в args — запуск модуля corporate_kb.mcp.server. Поэтому Qwen не зависит от глобальных python, uv, PATH, shell activation или wrapper-скрипта.

Минимальная форма server entry:

{
  "command": "/absolute/path/to/repository/.venv/bin/python",
  "args": ["-m", "corporate_kb.mcp.server"],
  "cwd": "/absolute/path/to/repository",
  "env": {
    "PYTHONPATH": "/absolute/path/to/repository/src"
  }
}

Альтернатива через CLI (выполняйте из корня этого репозитория, подставив абсолютные пути):

qwen mcp add \
  --scope project \
  --timeout 120000 \
  -e KB_KNOWLEDGE_DIR=/absolute/path/to/repository/knowledge \
  -e KB_CACHE_DIR=/absolute/path/to/repository/.cache/kb \
  -e KB_EMBEDDING_PROVIDER=hash \
  -e KB_EMBEDDING_LOCAL_FILES_ONLY=true \
  -e HF_HUB_OFFLINE=1 \
  -e TRANSFORMERS_OFFLINE=1 \
  -e PYTHONUNBUFFERED=1 \
  -e PYTHONNOUSERSITE=1 \
  -e PYTHONPATH=/absolute/path/to/repository/src \
  -e KB_AUTO_INDEX=false \
  local-corporate-kb \
  /absolute/path/to/repository/.venv/bin/python \
  -m corporate_kb.mcp.server

stdio — транспорт по умолчанию, поэтому --transport http здесь не нужен. Синтаксис команды сверен с официальной документацией Qwen Code, но в среде разработки этого репозитория qwen не был установлен, и команда локально не выполнялась. JSON-конфигурация также задаёт cwd и trust: false; статического фильтра tools в ней нет.

Проверка подключения:

qwen
/mcp

Тестовый запрос:

Используй corporate knowledge MCP.
Найди, какой сервис владеет дневными лимитами,
объясни правило и обязательно укажи использованные источники.

Встроенные tools сервера:

  • kb_search — поиск с top_k, min_score, metadata filters и компактными выдержками;

  • kb_get_chunk — лениво загружает один ограниченный фрагмент по chunk_id;

  • kb_run_context_benchmark — защищённый паролем read-only замер качества и сжатия;

  • kb_get_document — ограниченный извлекаемый фрагмент документа по document_id;

  • kb_list_documents — metadata документов без embeddings;

  • kb_stats — состояние индекса и абсолютные пути.

Не задавайте статический includeTools в Qwen settings, если используете управляемые tools из UI: клиентский allowlist скроет новые схемы от LLM. Прямой HTTP-клиент обновляет /mcp discovery, а однофайловый stdio proxy перечитывает каталог после перезапуска Qwen.

Ручной запуск stdio server:

KB_LOG_LEVEL=DEBUG ./.venv/bin/python -m corporate_kb.mcp.server

stdout зарезервирован для MCP-протокола; все application logs направляются в stderr.

Удалённый MCP по HTTP

Удалённый режим заранее загружает готовый индекс и только после этого открывает порт. Поэтому все подключённые Qwen CLI используют один прогретый процесс и не строят embeddings при каждом запросе. Endpoint реализует рекомендованный для удалённых MCP-серверов Streamable HTTP, а не устаревший SSE.

1. Подготовить сервер

Скопируйте репозиторий и документы на сервер, установите runtime и один раз постройте индекс:

cd /opt/corporate-kb
./scripts/setup-pip.sh --no-dev
./scripts/dev.sh index-hash

Сгенерируйте отдельный секрет длиной не менее 32 символов:

openssl rand -hex 32

Для прямого запуска внутри доверенной сети или VPN задайте секрет и запустите listener на всех сетевых интерфейсах:

export KB_MCP_HTTP_BEARER_TOKEN='PASTE_GENERATED_TOKEN'
export KB_MCP_HTTP_HOST='0.0.0.0'
export KB_MCP_HTTP_PORT='8000'
export KB_AUTO_INDEX='false'

./scripts/start-mcp-http.sh

Публичный health check не раскрывает тексты документов:

curl http://10.0.0.5:8000/health

Для stdio-клиента сервер также предоставляет защищённый read-only JSON API. Например, проверка поиска использует обычный GET и тот же Bearer-токен:

curl -G 'http://10.0.0.5:8000/api/v1/search' \
  -H 'Authorization: Bearer PASTE_GENERATED_TOKEN' \
  --data-urlencode 'query=какой сервис владеет дневными лимитами' \
  --data-urlencode 'top_k=3'

Доступны /api/v1/search, /api/v1/document, /api/v1/chunk, защищённый /api/v1/admin/context-benchmark, /api/v1/documents и /api/v1/stats. Они используют тот же прогретый индекс, что и MCP tools, не строят embeddings на клиенте и не изменяют документы.

Сам /mcp требует заголовок Authorization: Bearer .... Ограничения по Host, Origin, домену или IP нет: сервер принимает клиента с любого адреса, если передан правильный токен. KB_AUTO_INDEX=false гарантирует, что удалённый процесс не начнёт неожиданную переиндексацию.

Проверяйте с клиентской машины не только /health, но и настоящий MCP initialize:

curl -i --max-time 15 \
  'http://10.0.0.5:8000/mcp' \
  -H 'Authorization: Bearer PASTE_GENERATED_TOKEN' \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  --data '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"curl-test","version":"1.0"}}}'

Ожидается HTTP/1.1 200. 401 означает неверный токен, 404 — неверный путь, а 421 — что запущена старая сборка с Host allowlist. Прямой FastMCP listener, запущенный через Python или uv, использует обычный HTTP. https:// указывайте только при наличии TLS reverse proxy; иначе Qwen обычно сообщает TypeError: fetch failed.

2. Подключить Qwen CLI

Перед раздачей впишите в корневой install.sh публичный HTTPS-адрес сервера и созданный токен:

default_mcp_url="https://kb.company.example/mcp"
default_mcp_token="THE_SERVER_TOKEN"

Сотруднику передаётся только этот один файл. В любом каталоге он выполняет:

bash install.sh

Скрипт не скачивает репозиторий, документы или Python-зависимости и не создаёт каталог RAG. Он только добавляет подключение corporate-kb в пользовательскую конфигурацию уже установленного Qwen Code. После запуска сотрудник перезапускает qwen и проверяет соединение через /mcp.

Bearer-токен внутри готового скрипта является секретом: раздавайте файл через защищённый корпоративный канал. Для отзыва доступа замените токен на сервере и выпустите новый скрипт.

3. Доступ через интернет

Не передавайте Bearer-токен по открытому интернету через обычный HTTP. Оставьте backend на 127.0.0.1:8000, а наружу опубликуйте его как HTTPS через Nginx, Caddy, ingress или корпоративный API gateway:

export KB_MCP_HTTP_BEARER_TOKEN='PASTE_GENERATED_TOKEN'
export KB_MCP_HTTP_HOST='127.0.0.1'
./scripts/start-mcp-http.sh

Минимальные существенные параметры location для Nginx:

location /mcp {
    proxy_pass http://127.0.0.1:8000;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_buffering off;
    proxy_read_timeout 3600s;
}

После этого клиент подключается к https://kb.example.com/mcp. TLS-сертификат и сетевой доступ настраиваются на reverse proxy; порт 8000 не должен быть открыт наружу.

Когда документы изменились, выполните ./scripts/dev.sh index-hash и перезапустите HTTP-процесс. Уже работающий процесс намеренно продолжает обслуживать согласованную старую версию индекса до рестарта.

Добавление Confluence-страницы

Экспортируйте страницу в HTML либо сохраните её как Markdown и положите внутрь knowledge/. Поддерживаются .md, .markdown, .html, .htm, .txt. Скрытые каталоги, .git, .cache, __pycache__, node_modules, бинарные и неподдерживаемые файлы игнорируются. После изменения перестройте индекс; при обычном запуске несовпадение knowledge_hash также инвалидирует кэш.

Пример front matter:

---
document_type: service
service: limits-service
domain: payments
status: current
authority: confluence
authority_priority: 80
owner: limits-team
source_id: "confluence-12345"
source_url: "https://confluence.example.com/pages/12345"
last_reviewed: "2026-07-20"
custom_field: "неизвестные поля тоже сохраняются"
---

# Limits Service

Без front matter заголовок берётся из первого H1 или имени файла, source_id — из относительного пути, status=current, authority=local_file, authority_priority=50.

Кэш и конфигурация

Пересобрать кэш:

./scripts/dev.sh index

Полностью удалить его можно командой rm -rf .cache/kb, после чего снова выполнить kb index. Запись каждого файла атомарна, а manifest.json заменяется последним. Повреждение JSON/NumPy, несовпадение схемы, модели, dimension, query instruction, chunking config или knowledge hash приводит к понятной invalidation, а не к неясной NumPy-ошибке.

Все параметры перечислены в .env.example. Основные:

  • KB_EMBEDDING_PROVIDER=sentence_transformers|hash;

  • KB_EMBEDDING_MODEL=./models/Qwen3-Embedding-0.6B — локальный каталог model files;

  • KB_EMBEDDING_LOCAL_FILES_ONLY=true — fail-closed запрет сетевой загрузки модели;

  • KB_EMBEDDING_DEVICE=auto|cpu|mps|cuda;

  • KB_EMBEDDING_DIMENSION=1024;

  • KB_CHUNK_SIZE_TOKENS=700, KB_CHUNK_HARD_MAX_TOKENS=900, KB_CHUNK_OVERLAP_TOKENS=80;

  • KB_AUTO_INDEX=false.

  • KB_MCP_HTTP_HOST, KB_MCP_HTTP_PORT, KB_MCP_HTTP_PATH;

  • KB_MCP_HTTP_BEARER_TOKEN — обязательный секрет для HTTP-режима;

Относительные пути разрешаются относительно текущего project working directory; kb stats показывает итоговые абсолютные пути.

Проверки

./scripts/dev.sh lint
./scripts/dev.sh typecheck
./scripts/dev.sh test
./scripts/dev.sh check

Обычный shell-скрипт scripts/dev.sh также объединяет повседневные команды:

./scripts/dev.sh install
./scripts/dev.sh install-pip
./scripts/dev.sh install-semantic
./scripts/dev.sh install-pip-semantic
./scripts/dev.sh test
./scripts/dev.sh lint
./scripts/dev.sh typecheck
./scripts/dev.sh index-hash
./scripts/dev.sh search-hash
./scripts/dev.sh index
./scripts/dev.sh search
./scripts/dev.sh index-semantic
./scripts/dev.sh eval
./scripts/dev.sh serve
./scripts/dev.sh serve-http

Тесты всегда инжектируют hash provider и не требуют интернета, Hugging Face, GPU, Qwen Code, Docker или внешней БД. Интеграционные тесты проверяют как in-memory MCP transport, так и HTTP handshake через ASGI без открытия сетевого порта.

Ограничения MVP и развитие

  • Полный brute-force cosine scan подходит для небольшой локальной базы, но не для миллионов чанков.

  • При изменении документов неизменившиеся chunks и их embeddings переиспользуются из предыдущего кэша; полный пересчёт нужен только для новых или изменившихся chunks.

  • Нет Confluence REST API, OAuth, фоновой синхронизации и HTML-адаптеров под каждый вариант экспорта.

  • Нет reranker, hybrid/BM25 retrieval и отдельной оценки authority при ранжировании.

  • Точный token counter реальной модели не используется для предварительного chunking: интерфейс TokenCounter отделён, поэтому его можно подключить без связи chunker с SentenceTransformer.

  • Статический Bearer-токен даёт всем клиентам одинаковые права; для персональных учётных записей, отзыва сессий и аудита нужен внешний gateway/IdP либо полноценный OAuth.

Для перехода на настоящую Vector DB нужно реализовать PostgresKnowledgeStore или QdrantKnowledgeStore с тем же контрактом KnowledgeStore, выбрать реализацию при сборке KnowledgeService и сохранить API сервиса/MCP без изменений. Следующим этапом стоит добавить инкрементальный cache manifest, batch upsert, hybrid retrieval и production evaluation corpus.

Install Server
F
license - not found
A
quality
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

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

  • A
    license
    Not graded
    quality
    D
    maintenance
    A local RAG-powered documentation search system that uses vector embeddings and Qdrant to enable semantic search across markdown, HTML, and other file formats. It provides an MCP interface for AI tools like Cursor to intelligently query and retrieve information from local knowledge bases.
    MIT
  • F
    license
    Not graded
    quality
    C
    maintenance
    Hybrid semantic search (dense vector + BM25) over local knowledge bases and codebases, exposed as MCP tools for AI agents to search and list knowledge bases.
  • A
    license
    A
    quality
    D
    maintenance
    Local-first RAG indexing and semantic search MCP server. Enables document retrieval and context-aware queries using local embedding models.
    3
    14
    MIT
  • F
    license
    A
    quality
    B
    maintenance
    A local-first document retrieval engine that mounts as an MCP tool for agents to index files, search for relevant passages, and let the agent's own LLM answer.
    4

View all related MCP servers

Related MCP Connectors

  • Persistent memory and knowledge management for AI agents with semantic search and 50+ tools.

  • Universal memory for AI agents and tools. Save, organize and search context anywhere.

  • Hosted MCP memory: save sessions/decisions once, search from Claude, Cursor, ChatGPT. EU-hosted FTS.

View all MCP Connectors

Latest Blog Posts

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/arptra/kb-rag-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server