Skip to main content
Glama
README.md
# porta-rag-mcp — RAG knowledge base как MCP-tools для AI-клиента

MCP-сервер, который выставляет **RAG-базу знаний** (поиск по документам) как
**tools** для любого AI-клиента — Claude Desktop, Cursor, VS Code, ChatGPT.
AI-агент сам решает когда искать по базе и как использовать найденное.

Архитектура — «обёртка»: тонкий слой [FastMCP](https://gofastmcp.com) над
RAG-движком, реализующим фиксированный интерфейс (`retrieve` / `query` /
`get_stats` / `diagnostic`). Код RAG-движка не зависит от MCP и не меняется.

> **Демо-домен:** база знаний по ВЭД (таможенные процедуры, ТН ВЭД, документы
> для импорта/экспорта). Система доменно-независима — на входе любые текстовые
> документы в `rag_data/docs/`.

---

## Почему это интересно

Это пересечение **RAG** ( retrieval-слой) и **MCP** (стандарт подключения AI к
внешним инструментам). Главное — **оптимизация токенов**: поиск разделён на два
режима:

| Tool | LLM | Назначение | Токены |
|---|---|---|---|
| `search_knowledge_base` | **нет** | retrieval-фаза RAG: топ-k чанков | дёшево |
| `ask_knowledge_base` | да | поиск + связный LLM-ответ с цитатами | дороже |

`search_knowledge_base` — это RAG «без LLM»: чистый retrieval, возвращает
релевантные фрагменты без вызова чат-модели. Для справочных lookups модель
предпочитает его; `ask_knowledge_base` (с генерацией) — только когда нужен
связный ответ. Это та самая оптимизация из бенчмарков
(RAG ≈ $0.005/запрос vs MCP-only ≈ $0.17/запрос).

---

## Tools (все read-only — ничего не меняют в индексе)

| Tool | Параметры | Возвращает |
|---|---|---|
| `search_knowledge_base` | `question, top_k=3` | `[{source, text, chunk_id, sim}, ...]` |
| `ask_knowledge_base` | `question, top_k=3` | `{answer, sources, empty, not_found, latency_ms}` |
| `kb_stats` | — | `{available, collection_count, files, models, ...}` |
| `kb_diagnostic` | — | `{available, store, chunks, embed_model, ...}` |

---

## Два бэкенда

### 1. Demo (в этом репо) — TF-IDF, без ключей и LLM

`rag_engine.py` в корне репо — минимальный TF-IDF бэкенд на numpy. Запускается
standalone, без API-ключей и без LLM. `ask_knowledge_base` в demo-режиме собирает
ответ из топ-чанков с цитатами (без генерации). **Это для демонстрации
MCP-обёртки**, не для продакшена.

### 2. Production — FAISS + BM25 + rerank (Porta)

В продакшене этот же `mcp_porta_rag.py` работает рядом с production-`rag_engine.py`
(FAISS + BM25 + rerank, эмбеддинги BGE-M3/e5, чат-модель DeepSeek/Qwen).
`mcp_porta_rag.py` **не меняется** — он импортирует `rag_engine` с тем же
интерфейсом. Достаточно положить production-`rag_engine.py` рядом и положить
документы/индекс в `rag_data/`.

---

## Установка и запуск

```bash
pip install -r requirements.txt   # fastmcp, numpy

# 1. самопроверка бэкенда
python3 rag_engine.py

# 2. дебаг в MCP Inspector (браузерный UI, кликаешь tools мышкой)
fastmcp dev mcp_porta_rag.py

# 3. stdio-сервер для AI-клиента
fastmcp run mcp_porta_rag.py
```

Документы: положи `.txt`/`.md` в `rag_data/docs/` — они индексируются при первом
запросе (lazy). `.env` не требуется для demo-бэкенда.

---

## Подключение к Claude Desktop

`~/Library/Application Support/Claude/claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "porta-rag": {
      "command": "python3",
      "args": ["/path/to/porta-rag-mcp/mcp_porta_rag.py"]
    }
  }
}
```

Перезапустить Claude Desktop → модель видит 4 tools. Пример запроса:
«найди в базе, какие документы нужны для импорта» → Claude вызывает
`search_knowledge_base` и получает релевантные фрагменты.

---

## Гибрид с Notion (опционально)

Если в тот же конфиг добавить официальный `notion-mcp-server`, Claude сможет
комбинировать tools обоих серверов:
«найди в ВЭД-базе про код ТН ВЭД 0702 и запиши саммари в Notion»
→ `search_knowledge_base` (этот сервер) + `create_page` (Notion).
MCP-серверы не связаны друг с другом напрямую — они оба подключены к одному
AI-клиенту, который оркестрирует между ними.

---

## Структура

```
.
├── mcp_porta_rag.py        # MCP-сервер (FastMCP): 4 read-only tools
├── rag_engine.py           # demo TF-IDF бэкенд (без ключей/LLM)
├── rag_data/docs/          # исходные документы (.txt/.md)
│   ├── customs_procedures.txt
│   ├── tn_ved_payments.txt
│   └── ved_documents.txt
├── requirements.txt        # fastmcp, numpy
└── README.md
```

## Интерфейс RAG-бэкенда (для замены на production)

```python
def retrieve(question: str, k: int = 3, user_id=None) -> list[dict]
    # -> [{"source": str, "text": str, "chunk_id": int, "sim": float}, ...]
def query(question: str, k: int = 3, model=None, user_id=None, history=None) -> dict
    # -> {"answer": str, "sources": list, "empty": bool, "not_found": bool, "latency_ms": int}
def get_stats(user_id=None) -> dict
    # -> {"available": bool, "empty": bool, "collection_count": int, "files": [...], ...}
def diagnostic() -> dict
```

## Стек

| Слой | Технология |
|---|---|
| MCP | FastMCP 3.x (Python), stdio / Streamable HTTP |
| RAG (demo) | TF-IDF + косинус, numpy, без ключей |
| RAG (prod) | FAISS + BM25 + rerank, эмбеддинги BGE-M3/e5, DeepSeek/Qwen |

## Лицензия

MIT.