@znt/mcp 1.0.3 → 1.0.4

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 +1 -1
  2. package/index.js +63 -12
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -33,7 +33,7 @@
33
33
  | Инструмент | Описание |
34
34
  | :--- | :--- |
35
35
  | `znatok_semantic_search` | **Гибридный поиск (BM25 + Векторы + RRF)** по кодовой базе. Находит узлы, архитектурные роли (`controller`, `service`, `repository`), аннотации и связи. |
36
- | `znatok_find_similar` | **Поиск аналогов и дубликатов**. Находит схожие реализации, существующие абстракции и паттерны по фрагменту кода. |
36
+ | `znatok_find_similar` | **Мультифакторный поиск аналогов и дубликатов**. Находит схожие реализации по имени символа (`target`) или описанию (`query`) на основе векторного сходства, связей в графе (Jaccard) и AST-структур. |
37
37
  | `znatok_get_subgraph` | **Графовый анализ и трассировка вызовов**. Возвращает подграф зависимостей или трассировку потока данных от узла A к узлу B (`from` ➔ `to`) в формате Mermaid / JSON. |
38
38
  | `znatok_file_outline` | **Семантический атлас (оглавление) файла**. Мгновенно отдает список всех символов (функции, структуры, методы) с номерами строк и их ролями. |
39
39
  | `znatok_server_logs` | **Логи сервера Znt**. Мониторинг событий индексации и состояния графа в реальном времени. |
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.4",
4
4
  "description": "Model Context Protocol adapter for Znt",
5
5
  "main": "index.js",
6
6
  "type": "module",