answer42 0.2.28__tar.gz → 0.2.30__tar.gz

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 (58) hide show
  1. {answer42-0.2.28 → answer42-0.2.30}/PKG-INFO +1 -1
  2. {answer42-0.2.28 → answer42-0.2.30}/pyproject.toml +1 -1
  3. {answer42-0.2.28 → answer42-0.2.30}/src/cf/DataProcessors/MCPTestManager/Forms//320/244/320/276/321/200/320/274/320/260/Ext/Form/Module.bsl +252 -27
  4. {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/assets/MCPTestManager.cf +0 -0
  5. {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/protocol.py +1 -1
  6. {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/server.py +132 -17
  7. {answer42-0.2.28 → answer42-0.2.30}/.gitignore +0 -0
  8. {answer42-0.2.28 → answer42-0.2.30}/LICENSE +0 -0
  9. {answer42-0.2.28 → answer42-0.2.30}/README.md +0 -0
  10. {answer42-0.2.28 → answer42-0.2.30}/credentials.example.json +0 -0
  11. {answer42-0.2.28 → answer42-0.2.30}/docs/agent-installation.md +0 -0
  12. {answer42-0.2.28 → answer42-0.2.30}/docs/architecture.md +0 -0
  13. {answer42-0.2.28 → answer42-0.2.30}/docs/assets/answer42-logo.png +0 -0
  14. {answer42-0.2.28 → answer42-0.2.30}/docs/assets/platform42-logo.svg +0 -0
  15. {answer42-0.2.28 → answer42-0.2.30}/docs/installation.md +0 -0
  16. {answer42-0.2.28 → answer42-0.2.30}/scripts/build_cf.py +0 -0
  17. {answer42-0.2.28 → answer42-0.2.30}/scripts/build_pages.py +0 -0
  18. {answer42-0.2.28 → answer42-0.2.30}/scripts/e2e_stable.py +0 -0
  19. {answer42-0.2.28 → answer42-0.2.30}/scripts/openclaw_mcp_autoreload.py +0 -0
  20. {answer42-0.2.28 → answer42-0.2.30}/scripts/rag_cli.py +0 -0
  21. {answer42-0.2.28 → answer42-0.2.30}/src/cf/ConfigDumpInfo.xml +0 -0
  22. {answer42-0.2.28 → answer42-0.2.30}/src/cf/Configuration.xml +0 -0
  23. {answer42-0.2.28 → answer42-0.2.30}/src/cf/DataProcessors/MCPTestManager/Ext/ObjectModule.bsl +0 -0
  24. {answer42-0.2.28 → answer42-0.2.30}/src/cf/DataProcessors/MCPTestManager/Forms//320/244/320/276/321/200/320/274/320/260/Ext/Form.xml" +0 -0
  25. {answer42-0.2.28 → answer42-0.2.30}/src/cf/DataProcessors/MCPTestManager/Forms//320/244/320/276/321/200/320/274/320/260.xml" +0 -0
  26. {answer42-0.2.28 → answer42-0.2.30}/src/cf/DataProcessors/MCPTestManager.xml +0 -0
  27. {answer42-0.2.28 → answer42-0.2.30}/src/cf/Ext/ManagedApplicationModule.bsl +0 -0
  28. {answer42-0.2.28 → answer42-0.2.30}/src/cf/Languages//320/240/321/203/321/201/321/201/320/272/320/270/320/271.xml" +0 -0
  29. {answer42-0.2.28 → answer42-0.2.30}/src/client_cf/Catalogs//320/237/320/241_/320/222/320/273/320/260/320/264/320/265/320/273/320/265/321/206.xml" +0 -0
  30. {answer42-0.2.28 → answer42-0.2.30}/src/client_cf/Catalogs//320/237/320/241_/320/241/320/277/321/200/320/260/320/262/320/276/321/207/320/275/320/270/320/2721.xml" +0 -0
  31. {answer42-0.2.28 → answer42-0.2.30}/src/client_cf/ConfigDumpInfo.xml +0 -0
  32. {answer42-0.2.28 → answer42-0.2.30}/src/client_cf/Configuration.xml +0 -0
  33. {answer42-0.2.28 → answer42-0.2.30}/src/client_cf/Languages//320/240/321/203/321/201/321/201/320/272/320/270/320/271.xml" +0 -0
  34. {answer42-0.2.28 → answer42-0.2.30}/src/client_cf/Reports//320/237/320/241_/320/241/320/277/320/270/321/201/320/276/320/272/320/255/320/273/320/265/320/274/320/265/320/275/321/202/320/276/320/262/Ext/ManagerModule.bsl" +0 -0
  35. {answer42-0.2.28 → answer42-0.2.30}/src/client_cf/Reports//320/237/320/241_/320/241/320/277/320/270/321/201/320/276/320/272/320/255/320/273/320/265/320/274/320/265/320/275/321/202/320/276/320/262/Ext/ObjectModule.bsl" +0 -0
  36. {answer42-0.2.28 → answer42-0.2.30}/src/client_cf/Reports//320/237/320/241_/320/241/320/277/320/270/321/201/320/276/320/272/320/255/320/273/320/265/320/274/320/265/320/275/321/202/320/276/320/262/Templates//320/236/321/201/320/275/320/276/320/262/320/275/320/260/321/217/320/241/321/205/320/265/320/274/320/260/320/232/320/276/320/274/320/277/320/276/320/275/320/276/320/262/320/272/320/270/320/224/320/260/320/275/320/275/321/213/321/205/Ext/Template.xml" +0 -0
  37. {answer42-0.2.28 → answer42-0.2.30}/src/client_cf/Reports//320/237/320/241_/320/241/320/277/320/270/321/201/320/276/320/272/320/255/320/273/320/265/320/274/320/265/320/275/321/202/320/276/320/262/Templates//320/236/321/201/320/275/320/276/320/262/320/275/320/260/321/217/320/241/321/205/320/265/320/274/320/260/320/232/320/276/320/274/320/277/320/276/320/275/320/276/320/262/320/272/320/270/320/224/320/260/320/275/320/275/321/213/321/205.xml" +0 -0
  38. {answer42-0.2.28 → answer42-0.2.30}/src/client_cf/Reports//320/237/320/241_/320/241/320/277/320/270/321/201/320/276/320/272/320/255/320/273/320/265/320/274/320/265/320/275/321/202/320/276/320/262.xml" +0 -0
  39. {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/__init__.py +0 -0
  40. {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/assets/MCPTestClient.cf +0 -0
  41. {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/assets/__init__.py +0 -0
  42. {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/assets/skills/answer42/SKILL.md +0 -0
  43. {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/assets/skills/answer42-rag/SKILL.md +0 -0
  44. {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/bridge.py +0 -0
  45. {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/credentials.py +0 -0
  46. {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/os_support.py +0 -0
  47. {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/platform.py +0 -0
  48. {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/rag/__init__.py +0 -0
  49. {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/rag/detect.py +0 -0
  50. {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/rag/model.py +0 -0
  51. {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/rag/parsers.py +0 -0
  52. {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/rag/service.py +0 -0
  53. {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/rag/store.py +0 -0
  54. {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/recorder.py +0 -0
  55. {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/release_helper.py +0 -0
  56. {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/runtime.py +0 -0
  57. {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/skill_installer.py +0 -0
  58. {answer42-0.2.28 → answer42-0.2.30}/src/mcp_1c/window_control.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: answer42
3
- Version: 0.2.28
3
+ Version: 0.2.30
4
4
  Summary: Answer42 — The Answer to Life, Universe, and 1C — UI Driver. MCP-powered 1C:Enterprise UI automation: click, fill, navigate, test, and introspect managed forms through the test-client API
5
5
  Author: Marvin (AI Assistant), 42Clouds, and contributors
6
6
  Author-email: "Kosolapov Stanislav (proDOOMman)" <prodoomman@gmail.com>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "answer42"
3
- version = "0.2.28"
3
+ version = "0.2.30"
4
4
  description = "Answer42 — The Answer to Life, Universe, and 1C — UI Driver. MCP-powered 1C:Enterprise UI automation: click, fill, navigate, test, and introspect managed forms through the test-client API"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -240,7 +240,7 @@
240
240
 
241
241
  &НаКлиенте
242
242
  Функция ВерсияПротоколаМенеджераMCP()
243
- Возврат "2026.07.02.3";
243
+ Возврат "2026.07.02.4";
244
244
  КонецФункции
245
245
 
246
246
  &НаКлиенте
@@ -294,6 +294,8 @@
294
294
  Возврат НажатьКнопку(Параметры);
295
295
  ИначеЕсли Метод = "find_object" Тогда
296
296
  Возврат НайтиОбъектИнтерфейса(Параметры);
297
+ ИначеЕсли Метод = "search_objects" Тогда
298
+ Возврат НайтиВсеОбъектыИнтерфейса(Параметры);
297
299
  ИначеЕсли Метод = "get_object" Тогда
298
300
  Возврат ПолучитьОбъектПриложенияMCP(Параметры);
299
301
  ИначеЕсли Метод = "activate_object" Тогда
@@ -2565,6 +2567,51 @@
2565
2567
  Возврат Неопределено;
2566
2568
  КонецФункции
2567
2569
 
2570
+ &НаКлиенте
2571
+ Функция НайтиВсеОбъектыИнтерфейса(Параметры)
2572
+ Если ТестируемоеПриложение = Неопределено Тогда
2573
+ ВызватьИсключение "Тестируемое приложение не подключено.";
2574
+ КонецЕсли;
2575
+ АктивноеОкно = ТестируемоеПриложение.ПолучитьАктивноеОкно();
2576
+ Имя = СвойствоСтруктуры(Параметры, "name", "");
2577
+ Заголовок = СвойствоСтруктуры(Параметры, "title", "");
2578
+ ТипОбъекта = СвойствоСтруктуры(Параметры, "type", "");
2579
+ Глубина = СвойствоСтруктуры(Параметры, "max_depth", 8);
2580
+ Лимит = СвойствоСтруктуры(Параметры, "limit", 50);
2581
+ Найденные = Новый Массив;
2582
+ НайтиВсеОбъектыРекурсивно(АктивноеОкно, Имя, Заголовок, ТипОбъекта, 0, Глубина, Лимит, Найденные);
2583
+ Возврат Новый Структура("found,count,limit,objects", Найденные.Количество() > 0, Найденные.Количество(), Лимит, Найденные);
2584
+ КонецФункции
2585
+
2586
+ &НаКлиенте
2587
+ Процедура НайтиВсеОбъектыРекурсивно(РодительскийОбъект, Имя, Заголовок, ТипОбъекта, ТекущаяГлубина, МаксимальнаяГлубина, Лимит, Найденные)
2588
+ Если ТекущаяГлубина > МаксимальнаяГлубина Тогда
2589
+ Возврат;
2590
+ КонецЕсли;
2591
+ Если Найденные.Количество() >= Лимит Тогда
2592
+ Возврат;
2593
+ КонецЕсли;
2594
+ Попытка
2595
+ ИмяТекущего = БезопасноеСвойство(РодительскийОбъект, "Имя", "");
2596
+ ЗаголовокТекущего = БезопасныйЗаголовок(РодительскийОбъект);
2597
+ ТипТекущего = Строка(ТипЗнч(РодительскийОбъект));
2598
+ НормализованныйТип = НормализованноеИмяТипа(ТипТекущего);
2599
+ Если ОбъектСоответствует(ИмяТекущего, ЗаголовокТекущего, НормализованныйТип, Имя, Заголовок, ТипОбъекта) Тогда
2600
+ Найденные.Добавить(ОбъектВСтруктуру(РодительскийОбъект, 0, 0));
2601
+ КонецЕсли;
2602
+ Подчиненные = БезопасныеПодчиненные(РодительскийОбъект);
2603
+ Для Каждого Подчиненный Из Подчиненные Цикл
2604
+ Если Найденные.Количество() >= Лимит Тогда
2605
+ Прервать;
2606
+ КонецЕсли;
2607
+ НайтиВсеОбъектыРекурсивно(Подчиненный, Имя, Заголовок, ТипОбъекта, ТекущаяГлубина + 1, МаксимальнаяГлубина, Лимит, Найденные);
2608
+ КонецЦикла;
2609
+ Исключение
2610
+ Инфо = ИнформацияОбОшибке();
2611
+ Лог("Подавленное исключение: " + ПодробноеПредставлениеОшибки(Инфо));
2612
+ КонецПопытки;
2613
+ КонецПроцедуры
2614
+
2568
2615
  &НаКлиенте
2569
2616
  Функция ОбъектСоответствует(ИмяОбъекта, ЗаголовокОбъекта, ТипОбъекта, ИскомоеИмя, ИскомыйЗаголовок, ИскомыйТип)
2570
2617
  НормализованноеИмя = НРег(СокрЛП(ИмяОбъекта));
@@ -3727,7 +3774,7 @@
3727
3774
  Если Представления.Количество() > 0 Тогда
3728
3775
  ИндексВыбора = ИндексРелевантногоВариантаСпискаВыбораMCP(Представления, Значение);
3729
3776
  Если ИндексВыбора = Неопределено Тогда
3730
- Поле.ЗакрытьВыпадающийСписок();
3777
+ ОтменитьРедактированиеПоляЕслиНужноMCP(Поле);
3731
3778
  ВызватьИсключение "Выпадающий список открыт, но релевантный вариант для значения " + Значение + " не найден";
3732
3779
  КонецЕсли;
3733
3780
  ПредставлениеВыбираемогоВарианта = ПредставлениеВариантаСпискаВыбораMCP(Представления[ИндексВыбора]);
@@ -3783,7 +3830,13 @@
3783
3830
  КонецПопытки;
3784
3831
 
3785
3832
  Если ТекстовыйВводВыполнен Тогда
3786
- ВызватьИсключение "Не удалось установить поле " + ИмяПоля + ": ВвестиТекст выполнен, но значение не подтвердилось; текущее представление: " + НеподтвержденноеПредставлениеПослеВвода;
3833
+ // Для полей выбора, особенно редакторов ячеек таблиц настроек, ВвестиТекст()
3834
+ // может не менять представление напрямую, но после этого остаются доступны
3835
+ // штатные методы выбора из списка/меню. Не отменяем редактирование сразу:
3836
+ // сначала пробуем choice/menu fallback-и ниже. Если они тоже не сработают,
3837
+ // финальный обработчик ошибок отменит редактирование ячейки через
3838
+ // ЗакончитьРедактированиеСтроки(Истина).
3839
+ Ошибки.Добавить("ВвестиТекст=выполнен, но значение не подтвердилось; текущее представление: " + НеподтвержденноеПредставлениеПослеВвода);
3787
3840
  КонецЕсли;
3788
3841
 
3789
3842
  // Переключатели/радиокнопки и поля со списком выбора: после активации
@@ -3878,6 +3931,7 @@
3878
3931
  Ошибки.Добавить("УстановитьЗначение=" + ОписаниеОшибки());
3879
3932
  КонецПопытки;
3880
3933
 
3934
+ ОтменитьРедактированиеПоляЕслиНужноMCP(Поле);
3881
3935
  ВызватьИсключение "Не удалось установить поле " + ИмяПоля + ": " + СтрСоединить(Ошибки, "; ");
3882
3936
  КонецФункции
3883
3937
 
@@ -4210,9 +4264,76 @@
4210
4264
  Возврат Новый Структура("found,name,title,added", Ложь, ИмяТаблицы, ЗаголовокТаблицы, Ложь);
4211
4265
  КонецЕсли;
4212
4266
  Таблица.Активизировать();
4213
- Таблица.ДобавитьСтроку();
4214
- ТекущаяСтрока = Таблица.ПолучитьТекущуюСтроку();
4215
- Возврат Новый Структура("found,added,current_row", Истина, Истина, СоответствиеВСтруктуру(ТекущаяСтрока));
4267
+ ТекущаяСтрокаДоJSON = "";
4268
+ Попытка
4269
+ ТекущаяСтрокаДоJSON = ЗначениеВJSON(СоответствиеВСтруктуру(Таблица.ПолучитьТекущуюСтроку()));
4270
+ Исключение
4271
+ ТекущаяСтрокаДоJSON = "";
4272
+ КонецПопытки;
4273
+ ОшибкаДобавленияСтроки = "";
4274
+ Попытка
4275
+ Таблица.ДобавитьСтроку();
4276
+ ТекущаяСтрока = Таблица.ПолучитьТекущуюСтроку();
4277
+ ТекущаяСтрокаПослеJSON = ЗначениеВJSON(СоответствиеВСтруктуру(ТекущаяСтрока));
4278
+ РежимРедактирования = ТаблицаВРежимеРедактированияMCP(Таблица);
4279
+ Если РежимРедактирования Или ТекущаяСтрокаДоJSON <> ТекущаяСтрокаПослеJSON Тогда
4280
+ Возврат Новый Структура("found,added,method,editing,current_row", Истина, Истина, "api_add_row", РежимРедактирования, СоответствиеВСтруктуру(ТекущаяСтрока));
4281
+ КонецЕсли;
4282
+ Исключение
4283
+ ОшибкаДобавленияСтроки = ОписаниеОшибки();
4284
+ КонецПопытки;
4285
+
4286
+ КнопкаДобавления = НайтиКнопкуДобавленияСтрокиТаблицыMCP(АктивноеОкно, Таблица, ИмяТаблицы, Глубина);
4287
+ Если КнопкаДобавления <> Неопределено Тогда
4288
+ ИмяКнопки = БезопасноеСвойство(КнопкаДобавления, "Имя", "");
4289
+ ЗаголовокКнопки = БезопасныйЗаголовок(КнопкаДобавления);
4290
+ КнопкаДобавления.Нажать();
4291
+ НовоеОкно = ТестируемоеПриложение.ПолучитьАктивноеОкно();
4292
+ МодальноеОкноОткрыто = НовоеОкно <> Неопределено И НовоеОкно <> АктивноеОкно;
4293
+ Возврат Новый Структура("found,added,method,button_name,button_title,modal_opened,modal_title,api_add_error", Истина, Истина, "command_button", ИмяКнопки, ЗаголовокКнопки, МодальноеОкноОткрыто, ?(МодальноеОкноОткрыто, БезопасноеСвойство(НовоеОкно, "Заголовок", ""), ""), ОшибкаДобавленияСтроки);
4294
+ КонецЕсли;
4295
+ Если Не ПустаяСтрока(ОшибкаДобавленияСтроки) Тогда
4296
+ ВызватьИсключение ОшибкаДобавленияСтроки;
4297
+ КонецЕсли;
4298
+ Возврат Новый Структура("found,added,method,editing,current_row", Истина, Ложь, "api_add_row_no_effect", ТаблицаВРежимеРедактированияMCP(Таблица), СоответствиеВСтруктуру(Таблица.ПолучитьТекущуюСтроку()));
4299
+ КонецФункции
4300
+
4301
+ &НаКлиенте
4302
+ Функция НайтиКнопкуДобавленияСтрокиТаблицыMCP(АктивноеОкно, Таблица, ИмяТаблицы, Глубина)
4303
+ Кандидаты = Новый Массив;
4304
+ Если Не ПустаяСтрока(ИмяТаблицы) Тогда
4305
+ Кандидаты.Добавить(ИмяТаблицы + "ДобавитьЭлементПорядка");
4306
+ Кандидаты.Добавить(ИмяТаблицы + "ДобавитьЭлемент");
4307
+ Кандидаты.Добавить(ИмяТаблицы + "ДобавитьСтроку");
4308
+ Кандидаты.Добавить(ИмяТаблицы + "Добавить");
4309
+ КонецЕсли;
4310
+ Кандидаты.Добавить("ДобавитьЭлементПорядка");
4311
+ Кандидаты.Добавить("Добавить новый элемент порядка");
4312
+ Кандидаты.Добавить("Добавить");
4313
+
4314
+ Для Каждого ИмяКнопки Из Кандидаты Цикл
4315
+ Попытка
4316
+ Кнопка = НайтиКнопку(Таблица, ИмяКнопки, "", 0, Глубина);
4317
+ Если Кнопка <> Неопределено Тогда
4318
+ Возврат Кнопка;
4319
+ КонецЕсли;
4320
+ Исключение
4321
+ КонецПопытки;
4322
+ КонецЦикла;
4323
+
4324
+ Если АктивноеОкно <> Неопределено Тогда
4325
+ Для Каждого ИмяКнопки Из Кандидаты Цикл
4326
+ Попытка
4327
+ Кнопка = НайтиКнопку(АктивноеОкно, ИмяКнопки, "", 0, Глубина);
4328
+ Если Кнопка <> Неопределено Тогда
4329
+ Возврат Кнопка;
4330
+ КонецЕсли;
4331
+ Исключение
4332
+ КонецПопытки;
4333
+ КонецЦикла;
4334
+ КонецЕсли;
4335
+
4336
+ Возврат Неопределено;
4216
4337
  КонецФункции
4217
4338
 
4218
4339
  &НаКлиенте
@@ -4324,15 +4445,93 @@
4324
4445
  КонецФункции
4325
4446
 
4326
4447
  &НаКлиенте
4327
- Процедура ОбеспечитьРедактированиеТекущейСтрокиТаблицыMCP(Таблица)
4448
+ Функция ОбеспечитьРедактированиеТекущейСтрокиТаблицыMCP(Таблица)
4328
4449
  Таблица.Активизировать();
4450
+ УжеРедактируется = ТаблицаВРежимеРедактированияMCP(Таблица);
4451
+ Если УжеРедактируется Тогда
4452
+ Возврат Новый Структура("editing,changed,error", Истина, Ложь, "");
4453
+ КонецЕсли;
4329
4454
  Попытка
4330
- Если Не Таблица.ТекущийРежимРедактирование() Тогда
4331
- Таблица.ИзменитьСтроку();
4455
+ Таблица.ИзменитьСтроку();
4456
+ Возврат Новый Структура("editing,changed,error", ТаблицаВРежимеРедактированияMCP(Таблица), Истина, "");
4457
+ Исключение
4458
+ // После ДобавитьСтроку() таблица может быть в промежуточном состоянии
4459
+ // добавления/редактирования, где ИзменитьСтроку() уже неприменима
4460
+ // ("Неподходящее состояние элемента управления"), но редактор текущей
4461
+ // строки уже доступен. Не падаем здесь: дальше ищем поле редактора;
4462
+ // если его нет — вернём точную ошибку поиска/установки.
4463
+ Возврат Новый Структура("editing,changed,error", ТаблицаВРежимеРедактированияMCP(Таблица), Ложь, ОписаниеОшибки());
4464
+ КонецПопытки;
4465
+ КонецФункции
4466
+
4467
+ &НаКлиенте
4468
+ Функция ТаблицаВРежимеРедактированияMCP(Таблица)
4469
+ Попытка
4470
+ Возврат Таблица.ТекущийРежимРедактирование();
4471
+ Исключение
4472
+ Возврат Ложь;
4473
+ КонецПопытки;
4474
+ КонецФункции
4475
+
4476
+ &НаКлиенте
4477
+ Функция ЗавершитьРедактированиеСтрокиТаблицыMCP(Таблица)
4478
+ ОшибкаЗавершения = "";
4479
+ БылаВРедактировании = ТаблицаВРежимеРедактированияMCP(Таблица);
4480
+ Попытка
4481
+ Если БылаВРедактировании Тогда
4482
+ Таблица.ЗакончитьРедактированиеСтроки(Ложь);
4483
+ КонецЕсли;
4484
+ Исключение
4485
+ ОшибкаЗавершения = ОписаниеОшибки();
4486
+ КонецПопытки;
4487
+
4488
+ ОсталасьВРедактировании = ТаблицаВРежимеРедактированияMCP(Таблица);
4489
+ ОтменаВыполнена = Ложь;
4490
+ ОшибкаОтмены = "";
4491
+ Если ОсталасьВРедактировании Тогда
4492
+ Попытка
4493
+ Таблица.ЗакончитьРедактированиеСтроки(Истина);
4494
+ ОтменаВыполнена = Истина;
4495
+ Исключение
4496
+ ОшибкаОтмены = ОписаниеОшибки();
4497
+ КонецПопытки;
4498
+ КонецЕсли;
4499
+
4500
+ ВсеЕщеВРедактировании = ТаблицаВРежимеРедактированияMCP(Таблица);
4501
+ Возврат Новый Структура("was_editing,commit_error,remained_editing,cancel_done,cancel_error,still_editing", БылаВРедактировании, ОшибкаЗавершения, ОсталасьВРедактировании, ОтменаВыполнена, ОшибкаОтмены, ВсеЕщеВРедактировании);
4502
+ КонецФункции
4503
+
4504
+ &НаКлиенте
4505
+ Функция НайтиРодительскуюТаблицуMCP(Объект)
4506
+ Текущий = Объект;
4507
+ Пока Текущий <> Неопределено Цикл
4508
+ Если НормализованноеИмяТипа(ТипЗнч(Текущий)) = "тестируемаятаблицаформы" Тогда
4509
+ Возврат Текущий;
4510
+ КонецЕсли;
4511
+ Попытка
4512
+ Текущий = Текущий.ПолучитьРодителя();
4513
+ Исключение
4514
+ Возврат Неопределено;
4515
+ КонецПопытки;
4516
+ КонецЦикла;
4517
+ Возврат Неопределено;
4518
+ КонецФункции
4519
+
4520
+ &НаКлиенте
4521
+ Процедура ОтменитьРедактированиеПоляЕслиНужноMCP(Поле)
4522
+ Попытка
4523
+ Если ОткрытВыпадающийСписокПоляMCP(Поле) Тогда
4524
+ Поле.ЗакрытьВыпадающийСписок();
4332
4525
  КонецЕсли;
4333
4526
  Исключение
4334
- Таблица.ИзменитьСтроку();
4335
4527
  КонецПопытки;
4528
+ Таблица = НайтиРодительскуюТаблицуMCP(Поле);
4529
+ Если Таблица <> Неопределено И ТаблицаВРежимеРедактированияMCP(Таблица) Тогда
4530
+ Попытка
4531
+ Таблица.ЗакончитьРедактированиеСтроки(Истина);
4532
+ Исключение
4533
+ КонецПопытки;
4534
+ КонецЕсли;
4336
4535
  КонецПроцедуры
4337
4536
 
4338
4537
  &НаКлиенте
@@ -4407,11 +4606,12 @@
4407
4606
  ВызватьИсключение "Таблица не найдена: " + ИмяТаблицы;
4408
4607
  КонецЕсли;
4409
4608
 
4410
- ОбеспечитьРедактированиеТекущейСтрокиТаблицыMCP(Таблица);
4609
+ РезультатНачалаРедактирования = ОбеспечитьРедактированиеТекущейСтрокиТаблицыMCP(Таблица);
4411
4610
  АктивноеОкно = ТестируемоеПриложение.ПолучитьАктивноеОкно();
4412
4611
  Поле = НайтиПолеРедактораТаблицыMCP(АктивноеОкно, Таблица, ИмяТаблицы, ИмяПоля, ИскатьПоЗаголовку, МаксимальнаяГлубина);
4413
4612
  Если Поле = Неопределено Тогда
4414
- ВызватьИсключение "Поле редактора таблицы не найдено: " + ИмяПоля;
4613
+ ДополнениеОшибкиРедактирования = ?(ПустаяСтрока(РезультатНачалаРедактирования.error), "", "; ошибка начала редактирования: " + РезультатНачалаРедактирования.error);
4614
+ ВызватьИсключение "Поле редактора таблицы не найдено: " + ИмяПоля + ДополнениеОшибкиРедактирования;
4415
4615
  КонецЕсли;
4416
4616
  ИмяРедактора = БезопасноеСвойство(Поле, "Имя", "");
4417
4617
  Поле.Активизировать();
@@ -4429,14 +4629,13 @@
4429
4629
  ОшибкаУстановки = ОписаниеОшибки();
4430
4630
  КонецПопытки;
4431
4631
 
4432
- Попытка
4433
- Таблица.ЗакончитьРедактированиеСтроки(Ложь);
4434
- Исключение
4435
- Попытка
4436
- Таблица.ЗакончитьРедактированиеСтроки();
4437
- Исключение
4438
- КонецПопытки;
4439
- КонецПопытки;
4632
+ РезультатЗавершенияРедактирования = ЗавершитьРедактированиеСтрокиТаблицыMCP(Таблица);
4633
+ Если Не ПустаяСтрока(РезультатЗавершенияРедактирования.commit_error) Тогда
4634
+ ВызватьИсключение "Не удалось завершить редактирование строки таблицы " + ИмяТаблицы + ": " + РезультатЗавершенияРедактирования.commit_error;
4635
+ КонецЕсли;
4636
+ Если РезультатЗавершенияРедактирования.still_editing Тогда
4637
+ ВызватьИсключение "Таблица " + ИмяТаблицы + " осталась в режиме редактирования после попытки завершить и отменить редактирование строки";
4638
+ КонецЕсли;
4440
4639
 
4441
4640
  Проверка = ЗначениеКолонкиТекущейСтрокиMCP(Таблица, ИмяПоля, ИскатьПоЗаголовку);
4442
4641
  Проверено = Проверка.found И ЗначенияСовпадаютДляПроверкиMCP(Проверка.value, Значение);
@@ -4449,7 +4648,7 @@
4449
4648
  Если Не ПустаяСтрока(ОшибкаУстановки) Тогда
4450
4649
  МетодУстановки = МетодУстановки + "+verified_after_exception";
4451
4650
  КонецЕсли;
4452
- Возврат Новый Структура("set,table,field,value,editor_field,verified,verified_key,verified_value,method,current_row", Истина, ИмяТаблицы, ИмяПоля, Значение, ИмяРедактора, Проверено, Проверка.key, Проверка.value, МетодУстановки, СоответствиеВСтруктуру(Таблица.ПолучитьТекущуюСтроку()));
4651
+ Возврат Новый Структура("set,table,field,value,editor_field,verified,verified_key,verified_value,method,edit_start,edit_finish,current_row", Истина, ИмяТаблицы, ИмяПоля, Значение, ИмяРедактора, Проверено, Проверка.key, Проверка.value, МетодУстановки, РезультатНачалаРедактирования, РезультатЗавершенияРедактирования, СоответствиеВСтруктуру(Таблица.ПолучитьТекущуюСтроку()));
4453
4652
  КонецФункции
4454
4653
 
4455
4654
  &НаКлиенте
@@ -4466,9 +4665,10 @@
4466
4665
  Значения = СтруктураИзJSONПараметраMCP(Параметры, "values");
4467
4666
  ИмяТаблицы = СвойствоСтруктуры(Параметры, "table", "ДополнительнаяИнформация");
4468
4667
  ИскатьПоЗаголовку = СвойствоСтруктуры(Параметры, "search_by_title", Ложь);
4668
+ МаксимальнаяГлубина = СвойствоСтруктуры(Параметры, "max_depth", 20);
4469
4669
  Результаты = Новый Массив;
4470
4670
  Для Каждого КЗ Из Значения Цикл
4471
- ПараметрыПоля = Новый Структура("table,field,value,search_by_title", ИмяТаблицы, Строка(КЗ.Ключ), Строка(КЗ.Значение), ИскатьПоЗаголовку);
4671
+ ПараметрыПоля = Новый Структура("table,field,value,search_by_title,max_depth", ИмяТаблицы, Строка(КЗ.Ключ), Строка(КЗ.Значение), ИскатьПоЗаголовку, МаксимальнаяГлубина);
4472
4672
  Результаты.Добавить(УстановитьПолеТаблицыMCP(ПараметрыПоля));
4473
4673
  КонецЦикла;
4474
4674
  Возврат Новый Структура("set,table,values,results", Истина, ИмяТаблицы, Значения, Результаты);
@@ -4588,6 +4788,18 @@
4588
4788
  СписокКнопок.Добавить(Новый Структура("name,title", "", "Вывести список..."));
4589
4789
  СписокКнопок.Добавить(Новый Структура("name,title", "", "Вывести список"));
4590
4790
  РезультатКнопки = НажатьПервуюКнопкуОкна(СписокКнопок, СвойствоСтруктуры(Параметры, "max_depth", 8));
4791
+
4792
+ // After pressing "Вывести список", a dialog opens. Confirm it.
4793
+ КнопкиПодтверждения = Новый Массив;
4794
+ КнопкиПодтверждения.Добавить(Новый Структура("name,title", "", "Сформировать"));
4795
+ КнопкиПодтверждения.Добавить(Новый Структура("name,title", "", "ОК"));
4796
+ КнопкиПодтверждения.Добавить(Новый Структура("name,title", "", "Ok"));
4797
+ Попытка
4798
+ НажатьПервуюКнопкуОкна(КнопкиПодтверждения, СвойствоСтруктуры(Параметры, "max_depth", 8));
4799
+ Исключение
4800
+ // Dialog may not appear or may auto-close; proceed anyway.
4801
+ КонецПопытки;
4802
+
4591
4803
  Пауза = ТекущаяДата() + СвойствоСтруктуры(Параметры, "wait_seconds", 2);
4592
4804
  Пока ТекущаяДата() < Пауза Цикл КонецЦикла;
4593
4805
  Документы = ТабличныеДокументы(Новый Структура("include_text,max_depth", Истина, СвойствоСтруктуры(Параметры, "max_depth", 8)));
@@ -4614,7 +4826,7 @@
4614
4826
  КонецЕсли;
4615
4827
  Поле = СвойствоСтруктуры(Параметры, "field", "");
4616
4828
  Значение = СвойствоСтруктуры(Параметры, "value", "");
4617
- ВидСравнения = СвойствоСтруктуры(Параметры, "comparison", "Equal");
4829
+ ВидСравненияMCP = СвойствоСтруктуры(Параметры, "comparison", "Equal");
4618
4830
  Если ПустаяСтрока(Поле) Тогда
4619
4831
  ВызватьИсключение "Не задано поле отбора dynamic list.";
4620
4832
  КонецЕсли;
@@ -4643,7 +4855,7 @@
4643
4855
  Ошибки.Добавить("Левое значение: " + ОписаниеОшибки());
4644
4856
  КонецПопытки;
4645
4857
  Попытка
4646
- УстановитьЗначениеПоля(Новый Структура("field_name,value,search_by_title", "Вид сравнения", ВидСравнения, Истина));
4858
+ УстановитьЗначениеПоля(Новый Структура("field_name,value,search_by_title", "Вид сравнения", ВидСравненияMCP, Истина));
4647
4859
  Исключение
4648
4860
  Ошибки.Добавить("Вид сравнения: " + ОписаниеОшибки());
4649
4861
  КонецПопытки;
@@ -4659,9 +4871,9 @@
4659
4871
  КонецПопытки;
4660
4872
 
4661
4873
  Если Ошибки.Количество() > 0 Тогда
4662
- Возврат Новый Структура("applied,field,value,comparison,errors,settings_tree", Ложь, Поле, Значение, ВидСравнения, Ошибки, ДиагностикаДо);
4874
+ Возврат Новый Структура("applied,field,value,comparison,errors,settings_tree", Ложь, Поле, Значение, ВидСравненияMCP, Ошибки, ДиагностикаДо);
4663
4875
  КонецЕсли;
4664
- Возврат Новый Структура("applied,field,value,comparison", Истина, Поле, Значение, ВидСравнения);
4876
+ Возврат Новый Структура("applied,field,value,comparison", Истина, Поле, Значение, ВидСравненияMCP);
4665
4877
  КонецФункции
4666
4878
 
4667
4879
  &НаКлиенте
@@ -4961,7 +5173,13 @@
4961
5173
  Возврат Неопределено;
4962
5174
  КонецЕсли;
4963
5175
  Если ТекущаяГлубина > МаксимальнаяГлубина Тогда
4964
- Возврат Строка(Объект);
5176
+ РезультатОбрезки = Новый Структура;
5177
+ РезультатОбрезки.Вставить("truncated", Истина);
5178
+ РезультатОбрезки.Вставить("truncated_reason", "max_depth");
5179
+ РезультатОбрезки.Вставить("type", Строка(ТипЗнч(Объект)));
5180
+ РезультатОбрезки.Вставить("name", БезопасноеСвойство(Объект, "Имя", ""));
5181
+ РезультатОбрезки.Вставить("title", БезопасныйЗаголовок(Объект));
5182
+ Возврат РезультатОбрезки;
4965
5183
  КонецЕсли;
4966
5184
  СобственнаяВидимость = БезопасноеЗначениеМетода(Объект, "ТекущаяВидимость", БезопасноеСвойство(Объект, "Видимость", Истина));
4967
5185
  СобственнаяДоступность = БезопасноеЗначениеМетода(Объект, "ТекущаяДоступность", БезопасноеСвойство(Объект, "Доступность", Истина));
@@ -5036,18 +5254,25 @@
5036
5254
  КонецЕсли;
5037
5255
  Исключение
5038
5256
  КонецПопытки;
5257
+ СкрытыхДетей = 0;
5039
5258
  Для Каждого Ребенок Из Дети Цикл
5040
5259
  ИмяРебенка = БезопасноеСвойство(Ребенок, "Имя", "");
5041
5260
  Если Не ПоказыватьСкрытые И Не ПустаяСтрока(ИмяТекущейСтраницы) И ИмяРебенка <> ИмяТекущейСтраницы Тогда
5261
+ СкрытыхДетей = СкрытыхДетей + 1;
5042
5262
  Продолжить;
5043
5263
  КонецЕсли;
5044
5264
  РебенокДляСериализации = ?(Не ПустаяСтрока(ИмяТекущейСтраницы) И ИмяРебенка = ИмяТекущейСтраницы И ТекущаяСтраницаГруппы <> Неопределено, ТекущаяСтраницаГруппы, Ребенок);
5045
5265
  СтруктураРебенка = ОбъектВСтруктуру(РебенокДляСериализации, ТекущаяГлубина + 1, МаксимальнаяГлубина, ПоказыватьСкрытые, Видимость, Доступность, ТолькоПросмотр);
5046
5266
  Если СтруктураРебенка <> Неопределено Тогда
5047
5267
  МассивДетей.Добавить(СтруктураРебенка);
5268
+ Иначе
5269
+ СкрытыхДетей = СкрытыхДетей + 1;
5048
5270
  КонецЕсли;
5049
5271
  КонецЦикла;
5050
5272
  Результат.Вставить("children", МассивДетей);
5273
+ Если СкрытыхДетей > 0 Тогда
5274
+ Результат.Вставить("hidden_children_count", СкрытыхДетей);
5275
+ КонецЕсли;
5051
5276
  КонецЕсли;
5052
5277
 
5053
5278
  Возврат Результат;
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  from dataclasses import dataclass
4
4
  from typing import Any
5
5
 
6
- MANAGER_PROTOCOL_VERSION = "2026.07.02.3"
6
+ MANAGER_PROTOCOL_VERSION = "2026.07.02.4"
7
7
 
8
8
 
9
9
  @dataclass(slots=True)
@@ -44,7 +44,7 @@ _RAG = RagService()
44
44
  _RAG_ENABLED = os.getenv("ONEC_MCP_DISABLE_RAG", "").strip().lower() not in {"1", "true", "yes", "on"}
45
45
  _CREDENTIAL_STORE_ENABLED = os.getenv("ONEC_MCP_DISABLE_CREDENTIAL_STORE", "").strip().lower() not in {"1", "true", "yes", "on"}
46
46
  _DEV_TOOLS_ENABLED = os.getenv("ONEC_MCP_DEV_TOOLS", "").strip().lower() in {"1", "true", "yes", "on"}
47
- DEFAULT_SESSION = "default"
47
+ DEFAULT_SESSION = os.getenv("ONEC_MCP_DEFAULT_SESSION", f"default-{os.getpid()}")
48
48
  STATE_FILE = os_support.runtime_path("ONEC_MCP_SESSION_STATE", "sessions.json")
49
49
  AUTORELOAD_PID_FILE = os_support.runtime_path("ONEC_MCP_AUTORELOAD_PID_FILE", "answer42-autoreload.pid")
50
50
  AUTORELOAD_LOG_FILE = os_support.runtime_path("ONEC_MCP_AUTORELOAD_LOG_FILE", "answer42-autoreload.log")
@@ -978,6 +978,29 @@ def _walk_ui_tree(node: Any):
978
978
  yield from _walk_ui_tree(child)
979
979
 
980
980
 
981
+ def _summarize_ui_tree(tree: Any, max_depth: int, include_hidden: bool) -> dict[str, Any]:
982
+ """Return compact diagnostics about omitted/truncated UI tree parts."""
983
+ nodes = list(_walk_ui_tree(tree))
984
+ truncated_nodes = [node for node in nodes if node.get("truncated")]
985
+ hidden_children_total = 0
986
+ hidden_nodes = []
987
+ for node in nodes:
988
+ hidden_count = node.get("hidden_children_count", 0)
989
+ if isinstance(hidden_count, int):
990
+ hidden_children_total += hidden_count
991
+ if node.get("visible") is False:
992
+ hidden_nodes.append(node)
993
+ return {
994
+ "max_depth": max_depth,
995
+ "include_hidden": include_hidden,
996
+ "nodes_count": len(nodes),
997
+ "truncated": bool(truncated_nodes),
998
+ "truncated_nodes_count": len(truncated_nodes),
999
+ "hidden_children_count": hidden_children_total,
1000
+ "hidden_nodes_count": len(hidden_nodes),
1001
+ }
1002
+
1003
+
981
1004
  def _metadata_name_from_navigation_url(url: str) -> str:
982
1005
  """Best-effort conversion from e1cib/list/... to a metadata full name."""
983
1006
  value = str(url or "")
@@ -3120,6 +3143,8 @@ async def ui_tree(max_depth: Annotated[int, "Maximum UI tree depth"] = 4,
3120
3143
  omitted by default; pass include_hidden=true to inspect the full 1C form tree.
3121
3144
  """
3122
3145
  tree = await _bridge_call_recorded(session_id, "ui_tree", {"max_depth": max_depth, "include_hidden": include_hidden})
3146
+ if isinstance(tree, dict):
3147
+ tree["tree_summary"] = _summarize_ui_tree(tree, max_depth=max_depth, include_hidden=include_hidden)
3123
3148
  if include_rag_types:
3124
3149
  sess = _session(session_id)
3125
3150
  if _RAG_ENABLED and sess.rag_enabled:
@@ -3143,11 +3168,12 @@ async def command_bar(*,
3143
3168
  async def click_button(
3144
3169
  name: Annotated[str, "Button name or title text"],
3145
3170
  search_by_title: Annotated[bool, "If true, match visible title instead of object name"] = False,
3171
+ max_depth: Annotated[int, "Maximum search depth"] = 8,
3146
3172
  *,
3147
3173
  session_id: Annotated[str, "Session id"] = DEFAULT_SESSION,
3148
3174
  ) -> Any:
3149
3175
  """Find a button in the active UI tree and press it."""
3150
- return await _bridge_call_recorded(session_id, "click_button", {"name": name, "search_by_title": search_by_title})
3176
+ return await _bridge_call_recorded(session_id, "click_button", {"name": name, "search_by_title": search_by_title, "max_depth": max_depth})
3151
3177
 
3152
3178
 
3153
3179
  @mcp.tool()
@@ -3163,6 +3189,20 @@ async def find_object(
3163
3189
  return await _bridge_call_recorded(session_id, "find_object", {"name": name, "title": title, "type": type, "max_depth": max_depth})
3164
3190
 
3165
3191
 
3192
+ @mcp.tool()
3193
+ async def search_objects(
3194
+ name: Annotated[str, "Object technical name, exact match when specified"] = "",
3195
+ title: Annotated[str, "Object visible title/caption, exact match when specified"] = "",
3196
+ type: Annotated[str, "1C test object type, e.g. ТестируемаяФорма"] = "",
3197
+ limit: Annotated[int, "Maximum number of matched objects to return"] = 50,
3198
+ max_depth: Annotated[int, "Maximum search depth"] = 8,
3199
+ *,
3200
+ session_id: Annotated[str, "Session id"] = DEFAULT_SESSION,
3201
+ ) -> Any:
3202
+ """Find all UI objects matching name, title and/or 1C test object type."""
3203
+ return await _bridge_call_recorded(session_id, "search_objects", {"name": name, "title": title, "type": type, "limit": limit, "max_depth": max_depth})
3204
+
3205
+
3166
3206
  @mcp.tool()
3167
3207
  async def get_object(
3168
3208
  name: Annotated[str, "Object name pattern"] = "",
@@ -3297,7 +3337,11 @@ async def tabular_documents(
3297
3337
  *,
3298
3338
  session_id: Annotated[str, "Session id"] = DEFAULT_SESSION,
3299
3339
  ) -> Any:
3300
- """List tabular document fields with dimensions; optionally include a cells range."""
3340
+ """List tabular document fields with dimensions; optionally include a cells range.
3341
+
3342
+ include_text is intended for small/partial ranges. For large tabular
3343
+ documents, save/export the report to a file and analyze that file instead.
3344
+ """
3301
3345
  return await _bridge_call_recorded(session_id, "tabular_documents", {"include_text": include_text, "max_depth": max_depth, "row_start": row_start, "row_end": row_end, "column_start": column_start, "column_end": column_end, "max_rows": max_rows, "max_columns": max_columns})
3302
3346
 
3303
3347
 
@@ -3315,7 +3359,13 @@ async def tabular_document_text(
3315
3359
  *,
3316
3360
  session_id: Annotated[str, "Session id"] = DEFAULT_SESSION,
3317
3361
  ) -> Any:
3318
- """Return dimensions and a cells range of a tabular document field in the active window."""
3362
+ """Return dimensions and a cells range of a tabular document field in the active window.
3363
+
3364
+ Use this for small tables or partial reads (headers, totals, spot checks). For
3365
+ large reports/tabular documents, prefer tabular_document_save/report export
3366
+ and analyze the saved PDF/XLSX/TXT/HTML file instead of reading UI cells one
3367
+ by one.
3368
+ """
3319
3369
  return await _bridge_call_recorded(session_id, "tabular_document_text", {"name": name, "title": title, "max_depth": max_depth, "row_start": row_start, "row_end": row_end, "column_start": column_start, "column_end": column_end, "max_rows": max_rows, "max_columns": max_columns})
3320
3370
 
3321
3371
 
@@ -3885,30 +3935,88 @@ async def configuration_name_version_from_subsystems(
3885
3935
  ) -> dict[str, Any]:
3886
3936
  """Open the 'Subsystem versions' information register and return the main configuration name/version.
3887
3937
 
3888
- The tool reads `РегистрСведений.ВерсииПодсистем`, scans list rows and returns
3889
- the row where `Это основная конфигурация` is `Да`.
3938
+ The tool reads `РегистрСведений.ВерсииПодсистем` via the list output
3939
+ command so that columns available to the dynamic list, including
3940
+ `Это основная конфигурация`, are visible even when they are not displayed
3941
+ in the default form table.
3890
3942
  """
3891
3943
  navigation_link = "e1cib/list/РегистрСведений.ВерсииПодсистем"
3892
3944
  await _bridge_call_recorded(session_id, "open_navigation_link", {"navigation_link": navigation_link})
3893
- rows_result = await _bridge_call_recorded(session_id, "table_rows", {"name": "Список", "title": "", "limit": 200, "max_depth": 8})
3894
- rows = rows_result.get("rows", []) if isinstance(rows_result, dict) else []
3895
3945
 
3896
- parsed_rows = [_table_row_pairs(row) for row in rows]
3897
- main_rows = [row for row in parsed_rows if _row_value(row, "Это основная конфигурация") == "Да"]
3898
- main = main_rows[0] if main_rows else {}
3946
+ output_result = await _bridge_call_recorded(session_id, "dynamic_list_output", {
3947
+ "button": "",
3948
+ "wait_seconds": 3,
3949
+ "max_depth": 8,
3950
+ })
3951
+ tabular_docs = output_result.get("tabular_documents", {}) if isinstance(output_result, dict) else {}
3952
+ items = tabular_docs.get("items", []) if isinstance(tabular_docs, dict) else []
3953
+
3954
+ main: dict[str, str] = {}
3955
+ cells: list[list[str]] = []
3956
+ if items:
3957
+ doc_name = items[0].get("name", "")
3958
+ text_result = await _bridge_call_recorded(session_id, "tabular_document_text", {
3959
+ "name": doc_name,
3960
+ "max_rows": 50,
3961
+ "max_columns": 20,
3962
+ })
3963
+ cells = text_result.get("cells", []) if isinstance(text_result, dict) else []
3964
+ main = _find_main_configuration_in_tabular_cells(cells)
3899
3965
 
3900
3966
  return {
3901
3967
  "found": bool(main),
3902
- "configuration_name": _row_value(main, "Имя подсистемы"),
3903
- "configuration_version": _row_value(main, "Версия"),
3968
+ "configuration_name": main.get("configuration_name", ""),
3969
+ "configuration_version": main.get("configuration_version", ""),
3904
3970
  "main_configuration_row": main,
3905
- "main_configuration_rows_count": len(main_rows),
3906
- "rows_count": len(parsed_rows),
3971
+ "main_configuration_rows_count": 1 if main else 0,
3972
+ "rows_count": max(0, len(cells) - 1),
3907
3973
  "navigation_link": navigation_link,
3908
3974
  "source": "РегистрСведений.ВерсииПодсистем",
3909
3975
  }
3910
3976
 
3911
3977
 
3978
+ def _find_main_configuration_in_tabular_cells(cells: list[list[str]]) -> dict[str, str]:
3979
+ """Scan tabular document cells for the main configuration row."""
3980
+ if not cells or len(cells) < 2:
3981
+ return {}
3982
+
3983
+ header_row_idx = -1
3984
+ name_col = -1
3985
+ version_col = -1
3986
+ main_cfg_col = -1
3987
+ for i, row in enumerate(cells):
3988
+ local_name_col = local_version_col = local_main_cfg_col = -1
3989
+ for j, cell in enumerate(row):
3990
+ cell_norm = str(cell).strip()
3991
+ if cell_norm == "Имя подсистемы":
3992
+ local_name_col = j
3993
+ elif cell_norm == "Версия":
3994
+ local_version_col = j
3995
+ elif cell_norm == "Это основная конфигурация":
3996
+ local_main_cfg_col = j
3997
+ if local_name_col >= 0 and local_version_col >= 0 and local_main_cfg_col >= 0:
3998
+ header_row_idx = i
3999
+ name_col = local_name_col
4000
+ version_col = local_version_col
4001
+ main_cfg_col = local_main_cfg_col
4002
+ break
4003
+
4004
+ if header_row_idx < 0:
4005
+ return {}
4006
+
4007
+ for row in cells[header_row_idx + 1:]:
4008
+ if len(row) <= max(name_col, version_col, main_cfg_col):
4009
+ continue
4010
+ if _is_truthy_1c(str(row[main_cfg_col])):
4011
+ return {
4012
+ "configuration_name": str(row[name_col]).strip(),
4013
+ "configuration_version": str(row[version_col]).strip(),
4014
+ "Это основная конфигурация": str(row[main_cfg_col]).strip(),
4015
+ }
4016
+
4017
+ return {}
4018
+
4019
+
3912
4020
  @mcp.tool()
3913
4021
  async def installed_extensions_from_catalog(
3914
4022
  *,
@@ -4025,11 +4133,12 @@ async def table_set_row_values(
4025
4133
  table: Annotated[str, "Table technical name"],
4026
4134
  values: Annotated[str, "JSON object mapping column names/titles to values"],
4027
4135
  search_by_title: Annotated[bool, "If true, match visible title instead of object name"] = False,
4136
+ max_depth: Annotated[int, "Maximum UI search depth"] = 20,
4028
4137
  *,
4029
4138
  session_id: Annotated[str, "Session id"] = DEFAULT_SESSION,
4030
4139
  ) -> Any:
4031
4140
  """Set multiple fields in the current row of a form table and verify each value."""
4032
- return await _bridge_call_recorded(session_id, "table_set_row_values", {"table": table, "values": values, "search_by_title": search_by_title})
4141
+ return await _bridge_call_recorded(session_id, "table_set_row_values", {"table": table, "values": values, "search_by_title": search_by_title, "max_depth": max_depth})
4033
4142
 
4034
4143
 
4035
4144
  @mcp.tool()
@@ -4468,7 +4577,13 @@ def screenshot(
4468
4577
  *,
4469
4578
  session_id: Annotated[str, "Session id"] = DEFAULT_SESSION,
4470
4579
  ) -> dict[str, Any]:
4471
- """Capture a screenshot from the MCP host. Defaults to the active visible 1C window."""
4580
+ """Capture a screenshot from the MCP host. Defaults to the active visible 1C window.
4581
+
4582
+ Screenshots are for user evidence and UI diagnostics only. Do not use them
4583
+ to analyze report/table/tabular-document/list data; use structured tools
4584
+ such as tabular_document_text, tabular_documents, table_rows,
4585
+ dynamic_list_output, or report export/save tools instead.
4586
+ """
4472
4587
  output_path = Path(path)
4473
4588
  output_path.parent.mkdir(parents=True, exist_ok=True)
4474
4589
 
File without changes
File without changes
File without changes
File without changes
File without changes