Files
kompas3d-mcp/src/Kompas.Mcp.Host/Tools/DocumentTools.cs
T
mikhail 60cf26f739 refactor(core): правки по код-ревью (COM-утечки, безопасный shutdown, типобезопасность)
- PartModeler: ResetAsync/Dispose освобождают RCW и чистят реестр эскизов/операций;
  Editor освобождается после EndEdit (C1)
- DocumentTools: сброс реестра при create/open/close — нет устаревших id (C2)
- DocumentService: освобождение транзитных IDocuments/IKompasDocument (m9)
- KompasSession.Dispose: ограниченное ожидание 5с вместо вечного блока (M3)
- PartModeler.ExtrudeAsync: убран dynamic -> конкретные definition-типы;
  throughAll только для cut (M4)
- FeatureTools.extrude_cut: дефолт throughAll=false, без подмены depth (m7)
- tests: регрессия на сброс реестра (старый id недействителен)
- docs/OPEN_QUESTIONS.md: итоги ревью (исправлено/отложено)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 09:18:52 +03:00

74 lines
3.5 KiB
C#

using System.ComponentModel;
using Kompas.Mcp.Core;
using Kompas.Mcp.Core.Documents;
using Kompas.Mcp.Core.Modeling;
using ModelContextProtocol.Server;
namespace Kompas.Mcp.Host.Tools;
/// <summary>Инструменты управления документами.</summary>
[McpServerToolType]
public sealed class DocumentTools(KompasSession session, DocumentService docs, PartModeler modeler)
{
[McpServerTool(Name = "document_create")]
[Description("Создать новый документ и сделать его активным. type: part | assembly | drawing | fragment | specification | text.")]
public async Task<string> Create([Description("Тип документа")] string type)
{
await session.ConnectAsync();
var info = await docs.CreateAsync(DocumentTypes.Parse(type));
await modeler.ResetAsync(); // прежние id эскизов/операций недействительны
return $"Создан документ типа {info.Type}.";
}
[McpServerTool(Name = "document_open")]
[Description("Открыть документ КОМПАС с диска по полному пути.")]
public async Task<string> Open(
[Description("Полный путь к файлу")] string path,
[Description("Открыть только для чтения")] bool readOnly = false)
{
await session.ConnectAsync();
var info = await docs.OpenAsync(path, readOnly);
await modeler.ResetAsync();
return $"Открыт документ: {info.Name} ({info.Type}).";
}
[McpServerTool(Name = "document_save_as")]
[Description("Сохранить активный документ по указанному пути. Безопасно для новых документов. Расширения: .m3d (деталь), .a3d (сборка), .cdw (чертёж), .frw (фрагмент).")]
public async Task<string> SaveAs([Description("Полный путь файла с расширением")] string path)
{
await session.ConnectAsync();
await docs.SaveAsAsync(path);
return $"Сохранено: {path}";
}
[McpServerTool(Name = "document_save")]
[Description("Сохранить активный документ (для уже сохранённого ранее).")]
public async Task<string> Save()
{
await session.ConnectAsync();
await docs.SaveAsync();
return "Документ сохранён.";
}
[McpServerTool(Name = "document_close")]
[Description("Закрыть активный документ. save — сохранять ли изменения при закрытии.")]
public async Task<string> Close([Description("Сохранять изменения")] bool save = false)
{
await session.ConnectAsync();
var ok = await docs.CloseAsync(save);
await modeler.ResetAsync();
return ok ? "Документ закрыт." : "Не удалось закрыть документ.";
}
[McpServerTool(Name = "document_active")]
[Description("Сведения об активном документе.")]
public async Task<string> Active()
{
await session.ConnectAsync();
var i = await docs.GetActiveAsync();
if (i is null) return "Нет активного документа.";
var where = string.IsNullOrEmpty(i.PathName) ? "не сохранён" : i.PathName;
return $"{i.Type}: {i.Name} ({where}).";
}
}