From 230c534403b6146dd9e949bf373a9adedef5be2d Mon Sep 17 00:00:00 2001 From: Shahovalov MIkhail Date: Tue, 26 May 2026 00:04:31 +0300 Subject: [PATCH] =?UTF-8?q?fix(host):=20=D0=BA=D0=BE=D1=80=D1=80=D0=B5?= =?UTF-8?q?=D0=BA=D1=82=D0=BD=D1=8B=D0=B9=20=D0=B2=D0=BE=D0=B7=D0=B2=D1=80?= =?UTF-8?q?=D0=B0=D1=82=20=D1=81=D0=BD=D0=B8=D0=BC=D0=BA=D0=B0=20=D1=87?= =?UTF-8?q?=D0=B5=D1=80=D0=B5=D0=B7=20ImageContentBlock.FromBytes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Data хранит base64-байты; сырые PNG-байты надо отдавать через FromBytes, иначе изображение портится на проводе (0x89 -> U+FFFD) - проверено сквозным прогоном по MCP-протоколу: connect -> document_create -> sketch_create -> add_circle -> close -> extrude_boss -> model_snapshot, получен валидный PNG (~19 КБ) цилиндра - docs/OPEN_QUESTIONS.md: конкурентность вызовов MCP и нюанс FromBytes Co-Authored-By: Claude Opus 4.7 --- docs/OPEN_QUESTIONS.md | 13 +++++++++++++ src/Kompas.Mcp.Host/Tools/VisionTools.cs | 7 ++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/docs/OPEN_QUESTIONS.md b/docs/OPEN_QUESTIONS.md index 9d13625..e9c8812 100644 --- a/docs/OPEN_QUESTIONS.md +++ b/docs/OPEN_QUESTIONS.md @@ -32,6 +32,19 @@ ## Журнал решений по ходу (дополняется автоматически во время работы) +### ⚠️ Конкурентность вызовов MCP и состояние сессии +MCP-сервер обрабатывает запросы **конкурентно**. Цикл моделирования с реестром эскизов по id +(`sketch_create` → `sketch_add_*`) предполагает, что клиент **ждёт ответа** на каждый вызов перед +следующим (нормальное поведение LLM-клиентов). При «конвейерной» отправке зависимых вызовов +возможна гонка (id ещё не зарегистрирован). STA-диспетчер сериализует COM, но не порядок постановки задач. +**На проработку:** нужна ли серверная сериализация вызовов инструментов (очередь) для надёжности +stateful-сессии? Пока полагаемся на последовательное поведение клиента. + +### ✅ Возврат изображения: ImageContentBlock.FromBytes +Поле `ImageContentBlock.Data` хранит **base64-байты**, а не сырые. Сырые байты PNG нужно +передавать через `ImageContentBlock.FromBytes(bytes, mimeType)` — он сам кодирует. Прямое +присваивание `Data = rawBytes` портит изображение на проводе. Исправлено в VisionTools. + ### ✅ Снимок 3D: рендер через файл, а не resultArrayBytes `ksDocument3D.SaveAsToRasterFormat(fileName, param)` при **непустом** `fileName` возвращает TRUE, пишет корректный PNG на диск, но `param.resultArrayBytes` остаётся **пустым**. Поэтому diff --git a/src/Kompas.Mcp.Host/Tools/VisionTools.cs b/src/Kompas.Mcp.Host/Tools/VisionTools.cs index eb00409..7cc8f03 100644 --- a/src/Kompas.Mcp.Host/Tools/VisionTools.cs +++ b/src/Kompas.Mcp.Host/Tools/VisionTools.cs @@ -17,10 +17,7 @@ public sealed class VisionTools(KompasSession session, SnapshotService snapshot) { await session.ConnectAsync(); var snap = await snapshot.CaptureActiveModelAsync(resolution: resolution); - return new ImageContentBlock - { - Data = snap.Bytes, - MimeType = snap.MimeType, - }; + // FromBytes хранит сырые байты и сам кодирует их в base64 для поля Data. + return ImageContentBlock.FromBytes(snap.Bytes, snap.MimeType); } }