@znt/mcp 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +97 -16
  2. package/index.js +63 -12
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -1,14 +1,14 @@
1
- # Znt MCP Adapter (@znt/mcp)
1
+ # Znt MCP Adapter (@znt/mcp v1.1.0)
2
2
 
3
3
  Официальный адаптер **Model Context Protocol (MCP)** для системы локального понимания кодовых баз и графового анализа **Знаток (Znt)**.
4
4
 
5
- Позволяет внешним ИИ-агентам и LLM-клиентам (Claude Desktop, Antigravity IDE, Cursor, Windsurf, Zed и др.) осуществлять умный гибридный поиск по смыслу, получать оглавления файлов, строить графы вызовов и трассировать зависимости с 0ms LLM оверхедом и максимальной экономией контекстных токенов.
5
+ Позволяет внешним ИИ-агентам и LLM-клиентам (Claude Desktop, Antigravity IDE, Cursor, Windsurf, Zed, Codex и др.) осуществлять гибридный и мультифакторный поиск по смыслу, получать семантические оглавления файлов, строить графы вызовов и трассировать зависимости с прозрачным трекингом экономии контекстных токенов.
6
6
 
7
7
  ---
8
8
 
9
9
  ## ⚡ Быстрый старт (npx)
10
10
 
11
- Не требуется предварительная сборка или ручная установка. Для подключения добавьте адаптер в ваш конфигурационный файл MCP (например, `~/.gemini/config/mcp_config.json` или `claude_desktop_config.json`):
11
+ Не требуется предварительная сборка или ручная установка. Для подключения добавьте адаптер в ваш конфигурационный файл MCP (например, `~/.gemini/config/mcp_config.json`, `.cursor/mcp.json` или `claude_desktop_config.json`):
12
12
 
13
13
  ```json
14
14
  {
@@ -30,25 +30,106 @@
30
30
 
31
31
  Адаптер предоставляет 6 специализированных инструментов с автоматическим отслеживанием экономии контекста:
32
32
 
33
- | Инструмент | Описание |
34
- | :--- | :--- |
35
- | `znatok_semantic_search` | **Гибридный поиск (BM25 + Векторы + RRF)** по кодовой базе. Находит узлы, архитектурные роли (`controller`, `service`, `repository`), аннотации и связи. |
36
- | `znatok_find_similar` | **Поиск аналогов и дубликатов**. Находит схожие реализации, существующие абстракции и паттерны по фрагменту кода. |
37
- | `znatok_get_subgraph` | **Графовый анализ и трассировка вызовов**. Возвращает подграф зависимостей или трассировку потока данных от узла A к узлу B (`from` `to`) в формате Mermaid / JSON. |
38
- | `znatok_file_outline` | **Семантический атлас (оглавление) файла**. Мгновенно отдает список всех символов (функции, структуры, методы) с номерами строк и их ролями. |
39
- | `znatok_server_logs` | **Логи сервера Znt**. Мониторинг событий индексации и состояния графа в реальном времени. |
40
- | `znatok_mcp_stats` | **Статистика экономии токенов**. Показывает количество сэкономленных токенов контекста, метрики вызовов и коэффициент оптимизации (`savings_ratio`). |
33
+ ### 1. `znatok_semantic_search`
34
+ * **Назначение**: Семантический и гибридный поиск (BM25 + Векторы + RRF) по элементам кодовой базы.
35
+ * **Параметры**:
36
+ * `query` *(string, обязательный)*: Поисковый запрос на естественном языке.
37
+ * `limit` *(number)*: Максимальное число результатов (по умолчанию `10`, макс `100`).
38
+ * `callers_level` *(number)*: Глубина графа входящих вызовов (по умолчанию `3`).
39
+ * `callees_level` *(number)*: Глубина графа исходящих вызовов (по умолчанию `3`).
40
+ * `include_code` *(boolean)*: Включать ли фрагменты исходного кода узлов.
41
+ * `max_code_lines` *(number)*: Максимум строк кода на узел (по умолчанию `30`).
42
+ * `role` *(string)*: Фильтр по архитектурной роли (`"controller"`, `"service"`, `"repository"`, `"model"`).
43
+ * `type` *(string)*: Фильтр по типу узла AST (`"function"`, `"struct"`, `"class"`, `"interface"`).
44
+ * `file_pattern` *(string)*: Маска пути файла (например `"pkg/semantic/*"` или `"*.go"`).
45
+ * `hybrid` *(boolean)*: Использовать гибридный RRF поиск (BM25 + Векторы, по умолчанию `true`).
41
46
 
42
47
  ---
43
48
 
44
- ## 🚀 Особенности работы
49
+ ### 2. `znatok_find_similar`
50
+ * **Назначение**: Умный мультифакторный поиск аналогов и дубликатов кода (60% векторный косинус + 30% граф вызовов Jaccard + 10% AST-роли).
51
+ * **Параметры**:
52
+ * `target` *(string)*: Имя символа, функции или класса в проекте (например `"Server.runFullScan"`). При указании вектор берется из БД напрямую **без обращения к LLM**.
53
+ * `query` *(string)*: Текстовое описание или код для поиска аналогов (используется, если `target` не задан).
54
+ * `limit` *(number)*: Максимальное число результатов (по умолчанию `10`).
55
+ * `callers_level` / `callees_level` *(number)*: Глубина связанных вызовов (по умолчанию `3`).
56
+ * `include_code` *(boolean)* / `max_code_lines` *(number)*: Параметры включения кода (по умолчанию `30` строк).
57
+ * `role` / `type` / `file_pattern` *(string)*: Фильтры по архитектуре и путям файлов.
45
58
 
46
- 1. **Единая интеграция с Znt Core API**: Все поисковые, графовые и семантические запросы к кодовой базе выполняются через HTTP API и WebSocket запущенного сервера Znt (`ZNT_API_URL`, по умолчанию `http://localhost:8080`).
47
- 2. **Локальный Usage Tracking**: SQLite (`node:sqlite`) используется только для локального логирования метрик использования в `.znt/mcp_usage.db`, мгновенно рассчитывая объем сэкономленного контекста по сравнению с прямым чтением исходных файлов.
59
+ ---
60
+
61
+ ### 3. `znatok_get_subgraph`
62
+ * **Назначение**: Построение подграфа окрестностей символа/файла или трассировка вызовов от точки `from` к точке `to`.
63
+ * **Параметры**:
64
+ * `target` *(string)*: Символ или относительный путь файла для вывода его окрестностей.
65
+ * `from` / `to` *(string)*: Точки трассировки цепочки вызовов (DFS по графу).
66
+ * `depth` *(number)*: Глубина обхода подграфа (по умолчанию `2`).
67
+ * `max_nodes` *(number)*: Максимальное количество узлов подграфа (по умолчанию `30`).
68
+ * `format` *(string)*: Формат ответа (`"mermaid"` для Markdown-диаграмм или `"json"`, по умолчанию `"mermaid"`).
69
+
70
+ ---
71
+
72
+ ### 4. `znatok_file_outline`
73
+ * **Назначение**: Анатомический семантический атлас (оглавление) файла за 1 запрос без замусоривания контекста.
74
+ * **Параметры**:
75
+ * `path` *(string, обязательный)*: Путь к целевому файлу (например `"internal/engine/server.go"`).
76
+ * `include_code` *(boolean)*: Включать ли фрагменты исходного кода для каждого символа.
77
+
78
+ ---
79
+
80
+ ### 5. `znatok_server_logs`
81
+ * **Назначение**: Чтение системных логов и событий сервера Znt в реальном времени через WebSocket.
82
+ * **Параметры**: Отсутствуют.
83
+
84
+ ---
85
+
86
+ ### 6. `znatok_mcp_stats`
87
+ * **Назначение**: Детальная метрика работы MCP-сервера и статистика сэкономленных токенов из SQLite.
88
+ * **Параметры**: Отсутствуют.
89
+
90
+ ---
91
+
92
+ ## 📊 Структура ответа и трекинг контекста (`_meta`)
93
+
94
+ Каждый инструмент возвращает обёртку со служебным объектом `_meta`:
95
+
96
+ ```json
97
+ {
98
+ "_meta": {
99
+ "tool": "znatok_semantic_search",
100
+ "response_tokens": 420,
101
+ "execution_ms": 15,
102
+ "last_call_saved_tokens": 12500,
103
+ "session_saved_tokens": 34000,
104
+ "alternative_cost": {
105
+ "tokens": 12920,
106
+ "description": "объем 3 затрагиваемых файлов целиком",
107
+ "savings_ratio": "30.8x"
108
+ },
109
+ "data_sources": ["core_api:search"]
110
+ },
111
+ "result": [ ... ]
112
+ }
113
+ ```
114
+
115
+ ---
116
+
117
+ ## ⚙️ Переменные окружения
118
+
119
+ | Переменная | Назначение | Значение по умолчанию |
120
+ | :--- | :--- | :--- |
121
+ | `ZNT_API_URL` | Базовый URL запущенного сервера Znt (HTTP & WS) | `http://localhost:8080` |
122
+
123
+ ---
124
+
125
+ ## 💾 Хранение данных
126
+
127
+ Статистика вызовов и экономии токенов сохраняется локально в базе данных SQLite:
128
+ * Путь к БД: `.znt/mcp_usage.db` в корневом каталоге проекта.
48
129
 
49
130
  ---
50
131
 
51
132
  ## 📋 Системные требования
52
133
 
53
- * **Node.js** версии **v22.5.0** или новее.
54
- * Индексированный проект с папкой `.znt` в корневом каталоге репозитория.
134
+ * **Node.js** версии **v22.5.0** или новее (используется встроенный модуль `node:sqlite`).
135
+ * Запущенный локальный сервер Znt (`http://localhost:8080`).
package/index.js CHANGED
@@ -307,11 +307,12 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
307
307
  },
308
308
  {
309
309
  name: 'znatok_find_similar',
310
- description: 'Ищет элементы кода в базе знаний проекта, которые являются семантически близкими по назначению к заданному фрагменту текста или сигнатуре. Помогает находить дубликаты, аналогичные методы или связанные абстракции. Возвращает список объектов с относительными путями файлов и структурой графа.',
310
+ description: 'Ищет аналогичные элементы кода и дубликаты на основе комбинированного мультифакторного анализа (векторное косинусное сходство, графовые соседи Jaccard и совпадение типов AST/ролей). Позволяет передавать как имя существующего символа (target), так и фрагмент текста/кода (query).',
311
311
  inputSchema: {
312
312
  type: 'object',
313
313
  properties: {
314
- query: { type: 'string', description: 'Текстовый фрагмент или описание функции/класса для поиска аналогов' },
314
+ target: { type: 'string', description: 'Имя существующего символа, функции или класса в проекте для поиска аналогов (например: "Server.runFullScan"). Если указано, вектор берется напрямую из БД без запроса к LLM.' },
315
+ query: { type: 'string', description: 'Текстовый фрагмент или описание функции/класса для поиска аналогов (используется, если target не задан)' },
315
316
  limit: { type: 'number', description: 'Максимальное количество возвращаемых элементов (по умолчанию 10)' },
316
317
  callers_level: { type: 'number', description: 'Глубина вложенности входящего графа вызовов ("где вызывается"), по умолчанию 3' },
317
318
  callees_level: { type: 'number', description: 'Глубина вложенности исходящего графа вызовов ("где вызывают"), по умолчанию 3' },
@@ -319,22 +320,20 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
319
320
  max_code_lines: { type: 'number', description: 'Максимальное количество строк исходного кода (по умолчанию 30)' },
320
321
  role: { type: 'string', description: 'Фильтр по архитектурной роли компонента (например: "controller", "repository", "service", "model")' },
321
322
  type: { type: 'string', description: 'Фильтр по типу узла AST (например: "function", "struct", "class", "interface")' },
322
- file_pattern: { type: 'string', description: 'Шаблон/маска пути файла для фильтрации (например: "pkg/semantic/*" или "*.go")' },
323
- hybrid: { type: 'boolean', description: 'Использовать гибридный поиск (RRF: BM25/Лексика + Векторы, по умолчанию true)' }
324
- },
325
- required: ['query']
323
+ file_pattern: { type: 'string', description: 'Шаблон/маска пути файла для фильтрации (например: "pkg/semantic/*" или "*.go")' }
324
+ }
326
325
  },
327
326
  },
328
327
  {
329
328
  name: 'znatok_get_subgraph',
330
- description: 'Возвращает ориентированный подграф (в формате Mermaid или JSON) вокруг заданного символа/файла (target) или вычисляет трассу вызовов между двумя точками (from -> to). Позволяет быстро визуализировать и исследовать архитектуру компонентов, цепочки вызовов и зависимости за 1 запрос без использования LLM.',
329
+ description: 'Возвращает ориентированный подграф (в формате Mermaid или JSON) вокруг заданного символа/файла (target) или ищет цепочку вызовов между двумя точками (from + to). Режим target: строит окрестности символа на заданную глубину. Режим трассировки (from + to): DFS запускается от `to` вверх по входящим рёбрам и ищет пути, в которых встречается `from`. Ограничение: если from вызывает to не напрямую, а через промежуточный узел, рёбра могут не отобразиться — в таком случае edges будет пустым и вернутся два изолированных узла.',
331
330
  inputSchema: {
332
331
  type: 'object',
333
332
  properties: {
334
- target: { type: 'string', description: 'Имя символа, функции, класса или относительный путь файла для построения подграфа окрестностей (например: "SemanticService" или "server.go")' },
335
- from: { type: 'string', description: 'Стартовый символ/функция для поиска трассы вызовов (например: "main" или "handleSearch")' },
336
- to: { type: 'string', description: 'Конечный символ/функция для поиска трассы вызовов (например: "GetNodeContent")' },
337
- depth: { type: 'number', description: 'Глубина обхода подграфа (по умолчанию 2)' },
333
+ target: { type: 'string', description: 'Имя символа, функции, класса или относительный путь файла для построения подграфа окрестностей (например: "SemanticService" или "server.go"). Используется только если from/to не заданы.' },
334
+ from: { type: 'string', description: 'Фильтр: символ/функция, которая должна встречаться в путях вызовов, найденных от `to`. Алгоритм ищет пути где from предшествует to по цепочке. Требует совместного указания с `to`.' },
335
+ to: { type: 'string', description: 'Точка старта трассировки символ/функция, от которой DFS идёт вверх по входящим рёбрам (PredecessorMap). Именно `to` является началом обхода, а не концом. Требует совместного указания с `from`.' },
336
+ depth: { type: 'number', description: 'Глубина обхода подграфа (по умолчанию 2). В режиме трассировки используется как depth*2.' },
338
337
  max_nodes: { type: 'number', description: 'Максимальное количество узлов подграфа (по умолчанию 30)' },
339
338
  format: { type: 'string', description: 'Формат ответа: "mermaid" для диаграммы в Markdown или "json" (по умолчанию "mermaid")' }
340
339
  }
@@ -437,7 +436,59 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
437
436
  let sources = [];
438
437
 
439
438
  switch (toolName) {
440
- case 'znatok_find_similar':
439
+ case 'znatok_find_similar': {
440
+ const target = request.params.arguments?.target || request.params.arguments?.target_symbol || '';
441
+ const query = request.params.arguments?.query || request.params.arguments?.text || '';
442
+ if (!target && !query) {
443
+ result = 'Either target or query parameter is required.';
444
+ break;
445
+ }
446
+ const limit = Math.min(Math.max(1, parseInt(request.params.arguments?.limit, 10) || 10), 100);
447
+ const callersLevel = Math.min(Math.max(0, parseInt(request.params.arguments?.callers_level ?? request.params.arguments?.caller_level, 10) ?? 3), 10);
448
+ const calleesLevel = Math.min(Math.max(0, parseInt(request.params.arguments?.callees_level ?? request.params.arguments?.callee_level, 10) ?? 3), 10);
449
+ const includeCode = Boolean(request.params.arguments?.include_code);
450
+ const maxCodeLines = parseInt(request.params.arguments?.max_code_lines, 10) || 30;
451
+ const role = request.params.arguments?.role || '';
452
+ const nodeType = request.params.arguments?.type || '';
453
+ const filePattern = request.params.arguments?.file_pattern || '';
454
+
455
+ const apiParams = new URLSearchParams({
456
+ limit: limit.toString(),
457
+ callers_level: callersLevel.toString(),
458
+ callees_level: calleesLevel.toString(),
459
+ include_code: includeCode.toString(),
460
+ max_code_lines: maxCodeLines.toString(),
461
+ });
462
+ if (target) apiParams.set('target', target);
463
+ if (query) apiParams.set('q', query);
464
+ if (role) apiParams.set('role', role);
465
+ if (nodeType) apiParams.set('type', nodeType);
466
+ if (filePattern) apiParams.set('file_pattern', filePattern);
467
+
468
+ const rawResults = await callZntApi(`/api/find_similar?${apiParams.toString()}`);
469
+ let processed = Array.isArray(rawResults) ? rawResults.map(item => {
470
+ const relFile = toRelativePath(item.file || item.file_path || item.filePath);
471
+ const resItem = {
472
+ ...item,
473
+ file: relFile
474
+ };
475
+ if (includeCode && !resItem.code && resItem.file && resItem.start_line && resItem.end_line) {
476
+ let endLine = resItem.end_line;
477
+ if (maxCodeLines > 0 && (endLine - resItem.start_line + 1) > maxCodeLines) {
478
+ endLine = resItem.start_line + maxCodeLines - 1;
479
+ }
480
+ resItem.code = getSourceSnippet(resItem.file, resItem.start_line, endLine);
481
+ }
482
+ return resItem;
483
+ }) : rawResults;
484
+ if (Array.isArray(processed)) {
485
+ processed = processed.slice(0, limit);
486
+ }
487
+ result = processed;
488
+ sources = ['core_api:find_similar'];
489
+ break;
490
+ }
491
+
441
492
  case 'znatok_semantic_search': {
442
493
  const query = request.params.arguments?.query || request.params.arguments?.text;
443
494
  if (!query) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@znt/mcp",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "Model Context Protocol adapter for Znt",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -31,4 +31,4 @@
31
31
  "@modelcontextprotocol/sdk": "^1.0.1",
32
32
  "ws": "^8.21.1"
33
33
  }
34
- }
34
+ }