answer42 0.2.28__tar.gz → 0.2.29__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.29}/PKG-INFO +1 -1
  2. {answer42-0.2.28 → answer42-0.2.29}/pyproject.toml +1 -1
  3. {answer42-0.2.28 → answer42-0.2.29}/src/cf/DataProcessors/MCPTestManager/Forms//320/244/320/276/321/200/320/274/320/260/Ext/Form/Module.bsl +174 -22
  4. {answer42-0.2.28 → answer42-0.2.29}/src/mcp_1c/assets/MCPTestManager.cf +0 -0
  5. {answer42-0.2.28 → answer42-0.2.29}/src/mcp_1c/protocol.py +1 -1
  6. {answer42-0.2.28 → answer42-0.2.29}/src/mcp_1c/server.py +127 -14
  7. {answer42-0.2.28 → answer42-0.2.29}/.gitignore +0 -0
  8. {answer42-0.2.28 → answer42-0.2.29}/LICENSE +0 -0
  9. {answer42-0.2.28 → answer42-0.2.29}/README.md +0 -0
  10. {answer42-0.2.28 → answer42-0.2.29}/credentials.example.json +0 -0
  11. {answer42-0.2.28 → answer42-0.2.29}/docs/agent-installation.md +0 -0
  12. {answer42-0.2.28 → answer42-0.2.29}/docs/architecture.md +0 -0
  13. {answer42-0.2.28 → answer42-0.2.29}/docs/assets/answer42-logo.png +0 -0
  14. {answer42-0.2.28 → answer42-0.2.29}/docs/assets/platform42-logo.svg +0 -0
  15. {answer42-0.2.28 → answer42-0.2.29}/docs/installation.md +0 -0
  16. {answer42-0.2.28 → answer42-0.2.29}/scripts/build_cf.py +0 -0
  17. {answer42-0.2.28 → answer42-0.2.29}/scripts/build_pages.py +0 -0
  18. {answer42-0.2.28 → answer42-0.2.29}/scripts/e2e_stable.py +0 -0
  19. {answer42-0.2.28 → answer42-0.2.29}/scripts/openclaw_mcp_autoreload.py +0 -0
  20. {answer42-0.2.28 → answer42-0.2.29}/scripts/rag_cli.py +0 -0
  21. {answer42-0.2.28 → answer42-0.2.29}/src/cf/ConfigDumpInfo.xml +0 -0
  22. {answer42-0.2.28 → answer42-0.2.29}/src/cf/Configuration.xml +0 -0
  23. {answer42-0.2.28 → answer42-0.2.29}/src/cf/DataProcessors/MCPTestManager/Ext/ObjectModule.bsl +0 -0
  24. {answer42-0.2.28 → answer42-0.2.29}/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.29}/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.29}/src/cf/DataProcessors/MCPTestManager.xml +0 -0
  27. {answer42-0.2.28 → answer42-0.2.29}/src/cf/Ext/ManagedApplicationModule.bsl +0 -0
  28. {answer42-0.2.28 → answer42-0.2.29}/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.29}/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.29}/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.29}/src/client_cf/ConfigDumpInfo.xml +0 -0
  32. {answer42-0.2.28 → answer42-0.2.29}/src/client_cf/Configuration.xml +0 -0
  33. {answer42-0.2.28 → answer42-0.2.29}/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.29}/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.29}/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.29}/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.29}/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.29}/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.29}/src/mcp_1c/__init__.py +0 -0
  40. {answer42-0.2.28 → answer42-0.2.29}/src/mcp_1c/assets/MCPTestClient.cf +0 -0
  41. {answer42-0.2.28 → answer42-0.2.29}/src/mcp_1c/assets/__init__.py +0 -0
  42. {answer42-0.2.28 → answer42-0.2.29}/src/mcp_1c/assets/skills/answer42/SKILL.md +0 -0
  43. {answer42-0.2.28 → answer42-0.2.29}/src/mcp_1c/assets/skills/answer42-rag/SKILL.md +0 -0
  44. {answer42-0.2.28 → answer42-0.2.29}/src/mcp_1c/bridge.py +0 -0
  45. {answer42-0.2.28 → answer42-0.2.29}/src/mcp_1c/credentials.py +0 -0
  46. {answer42-0.2.28 → answer42-0.2.29}/src/mcp_1c/os_support.py +0 -0
  47. {answer42-0.2.28 → answer42-0.2.29}/src/mcp_1c/platform.py +0 -0
  48. {answer42-0.2.28 → answer42-0.2.29}/src/mcp_1c/rag/__init__.py +0 -0
  49. {answer42-0.2.28 → answer42-0.2.29}/src/mcp_1c/rag/detect.py +0 -0
  50. {answer42-0.2.28 → answer42-0.2.29}/src/mcp_1c/rag/model.py +0 -0
  51. {answer42-0.2.28 → answer42-0.2.29}/src/mcp_1c/rag/parsers.py +0 -0
  52. {answer42-0.2.28 → answer42-0.2.29}/src/mcp_1c/rag/service.py +0 -0
  53. {answer42-0.2.28 → answer42-0.2.29}/src/mcp_1c/rag/store.py +0 -0
  54. {answer42-0.2.28 → answer42-0.2.29}/src/mcp_1c/recorder.py +0 -0
  55. {answer42-0.2.28 → answer42-0.2.29}/src/mcp_1c/release_helper.py +0 -0
  56. {answer42-0.2.28 → answer42-0.2.29}/src/mcp_1c/runtime.py +0 -0
  57. {answer42-0.2.28 → answer42-0.2.29}/src/mcp_1c/skill_installer.py +0 -0
  58. {answer42-0.2.28 → answer42-0.2.29}/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.29
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.29"
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,6 +3830,7 @@
3783
3830
  КонецПопытки;
3784
3831
 
3785
3832
  Если ТекстовыйВводВыполнен Тогда
3833
+ ОтменитьРедактированиеПоляЕслиНужноMCP(Поле);
3786
3834
  ВызватьИсключение "Не удалось установить поле " + ИмяПоля + ": ВвестиТекст выполнен, но значение не подтвердилось; текущее представление: " + НеподтвержденноеПредставлениеПослеВвода;
3787
3835
  КонецЕсли;
3788
3836
 
@@ -3878,6 +3926,7 @@
3878
3926
  Ошибки.Добавить("УстановитьЗначение=" + ОписаниеОшибки());
3879
3927
  КонецПопытки;
3880
3928
 
3929
+ ОтменитьРедактированиеПоляЕслиНужноMCP(Поле);
3881
3930
  ВызватьИсключение "Не удалось установить поле " + ИмяПоля + ": " + СтрСоединить(Ошибки, "; ");
3882
3931
  КонецФункции
3883
3932
 
@@ -4324,15 +4373,93 @@
4324
4373
  КонецФункции
4325
4374
 
4326
4375
  &НаКлиенте
4327
- Процедура ОбеспечитьРедактированиеТекущейСтрокиТаблицыMCP(Таблица)
4376
+ Функция ОбеспечитьРедактированиеТекущейСтрокиТаблицыMCP(Таблица)
4328
4377
  Таблица.Активизировать();
4378
+ УжеРедактируется = ТаблицаВРежимеРедактированияMCP(Таблица);
4379
+ Если УжеРедактируется Тогда
4380
+ Возврат Новый Структура("editing,changed,error", Истина, Ложь, "");
4381
+ КонецЕсли;
4329
4382
  Попытка
4330
- Если Не Таблица.ТекущийРежимРедактирование() Тогда
4331
- Таблица.ИзменитьСтроку();
4383
+ Таблица.ИзменитьСтроку();
4384
+ Возврат Новый Структура("editing,changed,error", ТаблицаВРежимеРедактированияMCP(Таблица), Истина, "");
4385
+ Исключение
4386
+ // После ДобавитьСтроку() таблица может быть в промежуточном состоянии
4387
+ // добавления/редактирования, где ИзменитьСтроку() уже неприменима
4388
+ // ("Неподходящее состояние элемента управления"), но редактор текущей
4389
+ // строки уже доступен. Не падаем здесь: дальше ищем поле редактора;
4390
+ // если его нет — вернём точную ошибку поиска/установки.
4391
+ Возврат Новый Структура("editing,changed,error", ТаблицаВРежимеРедактированияMCP(Таблица), Ложь, ОписаниеОшибки());
4392
+ КонецПопытки;
4393
+ КонецФункции
4394
+
4395
+ &НаКлиенте
4396
+ Функция ТаблицаВРежимеРедактированияMCP(Таблица)
4397
+ Попытка
4398
+ Возврат Таблица.ТекущийРежимРедактирование();
4399
+ Исключение
4400
+ Возврат Ложь;
4401
+ КонецПопытки;
4402
+ КонецФункции
4403
+
4404
+ &НаКлиенте
4405
+ Функция ЗавершитьРедактированиеСтрокиТаблицыMCP(Таблица)
4406
+ ОшибкаЗавершения = "";
4407
+ БылаВРедактировании = ТаблицаВРежимеРедактированияMCP(Таблица);
4408
+ Попытка
4409
+ Если БылаВРедактировании Тогда
4410
+ Таблица.ЗакончитьРедактированиеСтроки(Ложь);
4332
4411
  КонецЕсли;
4333
4412
  Исключение
4334
- Таблица.ИзменитьСтроку();
4413
+ ОшибкаЗавершения = ОписаниеОшибки();
4335
4414
  КонецПопытки;
4415
+
4416
+ ОсталасьВРедактировании = ТаблицаВРежимеРедактированияMCP(Таблица);
4417
+ ОтменаВыполнена = Ложь;
4418
+ ОшибкаОтмены = "";
4419
+ Если ОсталасьВРедактировании Тогда
4420
+ Попытка
4421
+ Таблица.ЗакончитьРедактированиеСтроки(Истина);
4422
+ ОтменаВыполнена = Истина;
4423
+ Исключение
4424
+ ОшибкаОтмены = ОписаниеОшибки();
4425
+ КонецПопытки;
4426
+ КонецЕсли;
4427
+
4428
+ ВсеЕщеВРедактировании = ТаблицаВРежимеРедактированияMCP(Таблица);
4429
+ Возврат Новый Структура("was_editing,commit_error,remained_editing,cancel_done,cancel_error,still_editing", БылаВРедактировании, ОшибкаЗавершения, ОсталасьВРедактировании, ОтменаВыполнена, ОшибкаОтмены, ВсеЕщеВРедактировании);
4430
+ КонецФункции
4431
+
4432
+ &НаКлиенте
4433
+ Функция НайтиРодительскуюТаблицуMCP(Объект)
4434
+ Текущий = Объект;
4435
+ Пока Текущий <> Неопределено Цикл
4436
+ Если НормализованноеИмяТипа(ТипЗнч(Текущий)) = "тестируемаятаблицаформы" Тогда
4437
+ Возврат Текущий;
4438
+ КонецЕсли;
4439
+ Попытка
4440
+ Текущий = Текущий.ПолучитьРодителя();
4441
+ Исключение
4442
+ Возврат Неопределено;
4443
+ КонецПопытки;
4444
+ КонецЦикла;
4445
+ Возврат Неопределено;
4446
+ КонецФункции
4447
+
4448
+ &НаКлиенте
4449
+ Процедура ОтменитьРедактированиеПоляЕслиНужноMCP(Поле)
4450
+ Попытка
4451
+ Если ОткрытВыпадающийСписокПоляMCP(Поле) Тогда
4452
+ Поле.ЗакрытьВыпадающийСписок();
4453
+ КонецЕсли;
4454
+ Исключение
4455
+ КонецПопытки;
4456
+ Таблица = НайтиРодительскуюТаблицуMCP(Поле);
4457
+ Если Таблица <> Неопределено И ТаблицаВРежимеРедактированияMCP(Таблица) Тогда
4458
+ Попытка
4459
+ Таблица.ЗакончитьРедактированиеСтроки(Истина);
4460
+ Исключение
4461
+ КонецПопытки;
4462
+ КонецЕсли;
4336
4463
  КонецПроцедуры
4337
4464
 
4338
4465
  &НаКлиенте
@@ -4407,11 +4534,12 @@
4407
4534
  ВызватьИсключение "Таблица не найдена: " + ИмяТаблицы;
4408
4535
  КонецЕсли;
4409
4536
 
4410
- ОбеспечитьРедактированиеТекущейСтрокиТаблицыMCP(Таблица);
4537
+ РезультатНачалаРедактирования = ОбеспечитьРедактированиеТекущейСтрокиТаблицыMCP(Таблица);
4411
4538
  АктивноеОкно = ТестируемоеПриложение.ПолучитьАктивноеОкно();
4412
4539
  Поле = НайтиПолеРедактораТаблицыMCP(АктивноеОкно, Таблица, ИмяТаблицы, ИмяПоля, ИскатьПоЗаголовку, МаксимальнаяГлубина);
4413
4540
  Если Поле = Неопределено Тогда
4414
- ВызватьИсключение "Поле редактора таблицы не найдено: " + ИмяПоля;
4541
+ ДополнениеОшибкиРедактирования = ?(ПустаяСтрока(РезультатНачалаРедактирования.error), "", "; ошибка начала редактирования: " + РезультатНачалаРедактирования.error);
4542
+ ВызватьИсключение "Поле редактора таблицы не найдено: " + ИмяПоля + ДополнениеОшибкиРедактирования;
4415
4543
  КонецЕсли;
4416
4544
  ИмяРедактора = БезопасноеСвойство(Поле, "Имя", "");
4417
4545
  Поле.Активизировать();
@@ -4429,14 +4557,13 @@
4429
4557
  ОшибкаУстановки = ОписаниеОшибки();
4430
4558
  КонецПопытки;
4431
4559
 
4432
- Попытка
4433
- Таблица.ЗакончитьРедактированиеСтроки(Ложь);
4434
- Исключение
4435
- Попытка
4436
- Таблица.ЗакончитьРедактированиеСтроки();
4437
- Исключение
4438
- КонецПопытки;
4439
- КонецПопытки;
4560
+ РезультатЗавершенияРедактирования = ЗавершитьРедактированиеСтрокиТаблицыMCP(Таблица);
4561
+ Если Не ПустаяСтрока(РезультатЗавершенияРедактирования.commit_error) Тогда
4562
+ ВызватьИсключение "Не удалось завершить редактирование строки таблицы " + ИмяТаблицы + ": " + РезультатЗавершенияРедактирования.commit_error;
4563
+ КонецЕсли;
4564
+ Если РезультатЗавершенияРедактирования.still_editing Тогда
4565
+ ВызватьИсключение "Таблица " + ИмяТаблицы + " осталась в режиме редактирования после попытки завершить и отменить редактирование строки";
4566
+ КонецЕсли;
4440
4567
 
4441
4568
  Проверка = ЗначениеКолонкиТекущейСтрокиMCP(Таблица, ИмяПоля, ИскатьПоЗаголовку);
4442
4569
  Проверено = Проверка.found И ЗначенияСовпадаютДляПроверкиMCP(Проверка.value, Значение);
@@ -4449,7 +4576,7 @@
4449
4576
  Если Не ПустаяСтрока(ОшибкаУстановки) Тогда
4450
4577
  МетодУстановки = МетодУстановки + "+verified_after_exception";
4451
4578
  КонецЕсли;
4452
- Возврат Новый Структура("set,table,field,value,editor_field,verified,verified_key,verified_value,method,current_row", Истина, ИмяТаблицы, ИмяПоля, Значение, ИмяРедактора, Проверено, Проверка.key, Проверка.value, МетодУстановки, СоответствиеВСтруктуру(Таблица.ПолучитьТекущуюСтроку()));
4579
+ Возврат Новый Структура("set,table,field,value,editor_field,verified,verified_key,verified_value,method,edit_start,edit_finish,current_row", Истина, ИмяТаблицы, ИмяПоля, Значение, ИмяРедактора, Проверено, Проверка.key, Проверка.value, МетодУстановки, РезультатНачалаРедактирования, РезультатЗавершенияРедактирования, СоответствиеВСтруктуру(Таблица.ПолучитьТекущуюСтроку()));
4453
4580
  КонецФункции
4454
4581
 
4455
4582
  &НаКлиенте
@@ -4588,6 +4715,18 @@
4588
4715
  СписокКнопок.Добавить(Новый Структура("name,title", "", "Вывести список..."));
4589
4716
  СписокКнопок.Добавить(Новый Структура("name,title", "", "Вывести список"));
4590
4717
  РезультатКнопки = НажатьПервуюКнопкуОкна(СписокКнопок, СвойствоСтруктуры(Параметры, "max_depth", 8));
4718
+
4719
+ // After pressing "Вывести список", a dialog opens. Confirm it.
4720
+ КнопкиПодтверждения = Новый Массив;
4721
+ КнопкиПодтверждения.Добавить(Новый Структура("name,title", "", "Сформировать"));
4722
+ КнопкиПодтверждения.Добавить(Новый Структура("name,title", "", "ОК"));
4723
+ КнопкиПодтверждения.Добавить(Новый Структура("name,title", "", "Ok"));
4724
+ Попытка
4725
+ НажатьПервуюКнопкуОкна(КнопкиПодтверждения, СвойствоСтруктуры(Параметры, "max_depth", 8));
4726
+ Исключение
4727
+ // Dialog may not appear or may auto-close; proceed anyway.
4728
+ КонецПопытки;
4729
+
4591
4730
  Пауза = ТекущаяДата() + СвойствоСтруктуры(Параметры, "wait_seconds", 2);
4592
4731
  Пока ТекущаяДата() < Пауза Цикл КонецЦикла;
4593
4732
  Документы = ТабличныеДокументы(Новый Структура("include_text,max_depth", Истина, СвойствоСтруктуры(Параметры, "max_depth", 8)));
@@ -4614,7 +4753,7 @@
4614
4753
  КонецЕсли;
4615
4754
  Поле = СвойствоСтруктуры(Параметры, "field", "");
4616
4755
  Значение = СвойствоСтруктуры(Параметры, "value", "");
4617
- ВидСравнения = СвойствоСтруктуры(Параметры, "comparison", "Equal");
4756
+ ВидСравненияMCP = СвойствоСтруктуры(Параметры, "comparison", "Equal");
4618
4757
  Если ПустаяСтрока(Поле) Тогда
4619
4758
  ВызватьИсключение "Не задано поле отбора dynamic list.";
4620
4759
  КонецЕсли;
@@ -4643,7 +4782,7 @@
4643
4782
  Ошибки.Добавить("Левое значение: " + ОписаниеОшибки());
4644
4783
  КонецПопытки;
4645
4784
  Попытка
4646
- УстановитьЗначениеПоля(Новый Структура("field_name,value,search_by_title", "Вид сравнения", ВидСравнения, Истина));
4785
+ УстановитьЗначениеПоля(Новый Структура("field_name,value,search_by_title", "Вид сравнения", ВидСравненияMCP, Истина));
4647
4786
  Исключение
4648
4787
  Ошибки.Добавить("Вид сравнения: " + ОписаниеОшибки());
4649
4788
  КонецПопытки;
@@ -4659,9 +4798,9 @@
4659
4798
  КонецПопытки;
4660
4799
 
4661
4800
  Если Ошибки.Количество() > 0 Тогда
4662
- Возврат Новый Структура("applied,field,value,comparison,errors,settings_tree", Ложь, Поле, Значение, ВидСравнения, Ошибки, ДиагностикаДо);
4801
+ Возврат Новый Структура("applied,field,value,comparison,errors,settings_tree", Ложь, Поле, Значение, ВидСравненияMCP, Ошибки, ДиагностикаДо);
4663
4802
  КонецЕсли;
4664
- Возврат Новый Структура("applied,field,value,comparison", Истина, Поле, Значение, ВидСравнения);
4803
+ Возврат Новый Структура("applied,field,value,comparison", Истина, Поле, Значение, ВидСравненияMCP);
4665
4804
  КонецФункции
4666
4805
 
4667
4806
  &НаКлиенте
@@ -4961,7 +5100,13 @@
4961
5100
  Возврат Неопределено;
4962
5101
  КонецЕсли;
4963
5102
  Если ТекущаяГлубина > МаксимальнаяГлубина Тогда
4964
- Возврат Строка(Объект);
5103
+ РезультатОбрезки = Новый Структура;
5104
+ РезультатОбрезки.Вставить("truncated", Истина);
5105
+ РезультатОбрезки.Вставить("truncated_reason", "max_depth");
5106
+ РезультатОбрезки.Вставить("type", Строка(ТипЗнч(Объект)));
5107
+ РезультатОбрезки.Вставить("name", БезопасноеСвойство(Объект, "Имя", ""));
5108
+ РезультатОбрезки.Вставить("title", БезопасныйЗаголовок(Объект));
5109
+ Возврат РезультатОбрезки;
4965
5110
  КонецЕсли;
4966
5111
  СобственнаяВидимость = БезопасноеЗначениеМетода(Объект, "ТекущаяВидимость", БезопасноеСвойство(Объект, "Видимость", Истина));
4967
5112
  СобственнаяДоступность = БезопасноеЗначениеМетода(Объект, "ТекущаяДоступность", БезопасноеСвойство(Объект, "Доступность", Истина));
@@ -5036,18 +5181,25 @@
5036
5181
  КонецЕсли;
5037
5182
  Исключение
5038
5183
  КонецПопытки;
5184
+ СкрытыхДетей = 0;
5039
5185
  Для Каждого Ребенок Из Дети Цикл
5040
5186
  ИмяРебенка = БезопасноеСвойство(Ребенок, "Имя", "");
5041
5187
  Если Не ПоказыватьСкрытые И Не ПустаяСтрока(ИмяТекущейСтраницы) И ИмяРебенка <> ИмяТекущейСтраницы Тогда
5188
+ СкрытыхДетей = СкрытыхДетей + 1;
5042
5189
  Продолжить;
5043
5190
  КонецЕсли;
5044
5191
  РебенокДляСериализации = ?(Не ПустаяСтрока(ИмяТекущейСтраницы) И ИмяРебенка = ИмяТекущейСтраницы И ТекущаяСтраницаГруппы <> Неопределено, ТекущаяСтраницаГруппы, Ребенок);
5045
5192
  СтруктураРебенка = ОбъектВСтруктуру(РебенокДляСериализации, ТекущаяГлубина + 1, МаксимальнаяГлубина, ПоказыватьСкрытые, Видимость, Доступность, ТолькоПросмотр);
5046
5193
  Если СтруктураРебенка <> Неопределено Тогда
5047
5194
  МассивДетей.Добавить(СтруктураРебенка);
5195
+ Иначе
5196
+ СкрытыхДетей = СкрытыхДетей + 1;
5048
5197
  КонецЕсли;
5049
5198
  КонецЦикла;
5050
5199
  Результат.Вставить("children", МассивДетей);
5200
+ Если СкрытыхДетей > 0 Тогда
5201
+ Результат.Вставить("hidden_children_count", СкрытыхДетей);
5202
+ КонецЕсли;
5051
5203
  КонецЕсли;
5052
5204
 
5053
5205
  Возврат Результат;
@@ -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)
@@ -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:
@@ -3163,6 +3188,20 @@ async def find_object(
3163
3188
  return await _bridge_call_recorded(session_id, "find_object", {"name": name, "title": title, "type": type, "max_depth": max_depth})
3164
3189
 
3165
3190
 
3191
+ @mcp.tool()
3192
+ async def search_objects(
3193
+ name: Annotated[str, "Object technical name, exact match when specified"] = "",
3194
+ title: Annotated[str, "Object visible title/caption, exact match when specified"] = "",
3195
+ type: Annotated[str, "1C test object type, e.g. ТестируемаяФорма"] = "",
3196
+ limit: Annotated[int, "Maximum number of matched objects to return"] = 50,
3197
+ max_depth: Annotated[int, "Maximum search depth"] = 8,
3198
+ *,
3199
+ session_id: Annotated[str, "Session id"] = DEFAULT_SESSION,
3200
+ ) -> Any:
3201
+ """Find all UI objects matching name, title and/or 1C test object type."""
3202
+ return await _bridge_call_recorded(session_id, "search_objects", {"name": name, "title": title, "type": type, "limit": limit, "max_depth": max_depth})
3203
+
3204
+
3166
3205
  @mcp.tool()
3167
3206
  async def get_object(
3168
3207
  name: Annotated[str, "Object name pattern"] = "",
@@ -3297,7 +3336,11 @@ async def tabular_documents(
3297
3336
  *,
3298
3337
  session_id: Annotated[str, "Session id"] = DEFAULT_SESSION,
3299
3338
  ) -> Any:
3300
- """List tabular document fields with dimensions; optionally include a cells range."""
3339
+ """List tabular document fields with dimensions; optionally include a cells range.
3340
+
3341
+ include_text is intended for small/partial ranges. For large tabular
3342
+ documents, save/export the report to a file and analyze that file instead.
3343
+ """
3301
3344
  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
3345
 
3303
3346
 
@@ -3315,7 +3358,13 @@ async def tabular_document_text(
3315
3358
  *,
3316
3359
  session_id: Annotated[str, "Session id"] = DEFAULT_SESSION,
3317
3360
  ) -> Any:
3318
- """Return dimensions and a cells range of a tabular document field in the active window."""
3361
+ """Return dimensions and a cells range of a tabular document field in the active window.
3362
+
3363
+ Use this for small tables or partial reads (headers, totals, spot checks). For
3364
+ large reports/tabular documents, prefer tabular_document_save/report export
3365
+ and analyze the saved PDF/XLSX/TXT/HTML file instead of reading UI cells one
3366
+ by one.
3367
+ """
3319
3368
  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
3369
 
3321
3370
 
@@ -3885,30 +3934,88 @@ async def configuration_name_version_from_subsystems(
3885
3934
  ) -> dict[str, Any]:
3886
3935
  """Open the 'Subsystem versions' information register and return the main configuration name/version.
3887
3936
 
3888
- The tool reads `РегистрСведений.ВерсииПодсистем`, scans list rows and returns
3889
- the row where `Это основная конфигурация` is `Да`.
3937
+ The tool reads `РегистрСведений.ВерсииПодсистем` via the list output
3938
+ command so that columns available to the dynamic list, including
3939
+ `Это основная конфигурация`, are visible even when they are not displayed
3940
+ in the default form table.
3890
3941
  """
3891
3942
  navigation_link = "e1cib/list/РегистрСведений.ВерсииПодсистем"
3892
3943
  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
3944
 
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 {}
3945
+ output_result = await _bridge_call_recorded(session_id, "dynamic_list_output", {
3946
+ "button": "",
3947
+ "wait_seconds": 3,
3948
+ "max_depth": 8,
3949
+ })
3950
+ tabular_docs = output_result.get("tabular_documents", {}) if isinstance(output_result, dict) else {}
3951
+ items = tabular_docs.get("items", []) if isinstance(tabular_docs, dict) else []
3952
+
3953
+ main: dict[str, str] = {}
3954
+ cells: list[list[str]] = []
3955
+ if items:
3956
+ doc_name = items[0].get("name", "")
3957
+ text_result = await _bridge_call_recorded(session_id, "tabular_document_text", {
3958
+ "name": doc_name,
3959
+ "max_rows": 50,
3960
+ "max_columns": 20,
3961
+ })
3962
+ cells = text_result.get("cells", []) if isinstance(text_result, dict) else []
3963
+ main = _find_main_configuration_in_tabular_cells(cells)
3899
3964
 
3900
3965
  return {
3901
3966
  "found": bool(main),
3902
- "configuration_name": _row_value(main, "Имя подсистемы"),
3903
- "configuration_version": _row_value(main, "Версия"),
3967
+ "configuration_name": main.get("configuration_name", ""),
3968
+ "configuration_version": main.get("configuration_version", ""),
3904
3969
  "main_configuration_row": main,
3905
- "main_configuration_rows_count": len(main_rows),
3906
- "rows_count": len(parsed_rows),
3970
+ "main_configuration_rows_count": 1 if main else 0,
3971
+ "rows_count": max(0, len(cells) - 1),
3907
3972
  "navigation_link": navigation_link,
3908
3973
  "source": "РегистрСведений.ВерсииПодсистем",
3909
3974
  }
3910
3975
 
3911
3976
 
3977
+ def _find_main_configuration_in_tabular_cells(cells: list[list[str]]) -> dict[str, str]:
3978
+ """Scan tabular document cells for the main configuration row."""
3979
+ if not cells or len(cells) < 2:
3980
+ return {}
3981
+
3982
+ header_row_idx = -1
3983
+ name_col = -1
3984
+ version_col = -1
3985
+ main_cfg_col = -1
3986
+ for i, row in enumerate(cells):
3987
+ local_name_col = local_version_col = local_main_cfg_col = -1
3988
+ for j, cell in enumerate(row):
3989
+ cell_norm = str(cell).strip()
3990
+ if cell_norm == "Имя подсистемы":
3991
+ local_name_col = j
3992
+ elif cell_norm == "Версия":
3993
+ local_version_col = j
3994
+ elif cell_norm == "Это основная конфигурация":
3995
+ local_main_cfg_col = j
3996
+ if local_name_col >= 0 and local_version_col >= 0 and local_main_cfg_col >= 0:
3997
+ header_row_idx = i
3998
+ name_col = local_name_col
3999
+ version_col = local_version_col
4000
+ main_cfg_col = local_main_cfg_col
4001
+ break
4002
+
4003
+ if header_row_idx < 0:
4004
+ return {}
4005
+
4006
+ for row in cells[header_row_idx + 1:]:
4007
+ if len(row) <= max(name_col, version_col, main_cfg_col):
4008
+ continue
4009
+ if _is_truthy_1c(str(row[main_cfg_col])):
4010
+ return {
4011
+ "configuration_name": str(row[name_col]).strip(),
4012
+ "configuration_version": str(row[version_col]).strip(),
4013
+ "Это основная конфигурация": str(row[main_cfg_col]).strip(),
4014
+ }
4015
+
4016
+ return {}
4017
+
4018
+
3912
4019
  @mcp.tool()
3913
4020
  async def installed_extensions_from_catalog(
3914
4021
  *,
@@ -4468,7 +4575,13 @@ def screenshot(
4468
4575
  *,
4469
4576
  session_id: Annotated[str, "Session id"] = DEFAULT_SESSION,
4470
4577
  ) -> dict[str, Any]:
4471
- """Capture a screenshot from the MCP host. Defaults to the active visible 1C window."""
4578
+ """Capture a screenshot from the MCP host. Defaults to the active visible 1C window.
4579
+
4580
+ Screenshots are for user evidence and UI diagnostics only. Do not use them
4581
+ to analyze report/table/tabular-document/list data; use structured tools
4582
+ such as tabular_document_text, tabular_documents, table_rows,
4583
+ dynamic_list_output, or report export/save tools instead.
4584
+ """
4472
4585
  output_path = Path(path)
4473
4586
  output_path.parent.mkdir(parents=True, exist_ok=True)
4474
4587
 
File without changes
File without changes
File without changes
File without changes
File without changes