From bddda87857a6a683c6b4fb8663fac07b35e547f6 Mon Sep 17 00:00:00 2001 From: Neel Shah Date: Tue, 21 Oct 2025 16:18:04 -0700 Subject: [PATCH 1/7] feat: integrate MCP functionality and clean up sample commands - Remove SampleCommandSet directory and all sample command files - Update CommandSetSettingsPage.xaml.cs for MCP integration - Update project file for new dependencies - Remove Chinese README file --- README_zh.md | 131 --------- .../Access/GetAvailableFamilyTypesCommand.cs | 53 ---- .../GetAvailableFamilyTypesEventHandler.cs | 137 ---------- .../Access/GetCurrentViewElementsCommand.cs | 52 ---- .../GetCurrentViewElementsEventHandler.cs | 248 ------------------ .../Access/GetCurrentViewInfoCommand.cs | 33 --- .../Access/GetCurrentViewInfoEventHandler.cs | 60 ----- .../Access/GetSelectedElementsCommand.cs | 48 ---- .../Access/GetSelectedElementsEventHandler.cs | 73 ------ .../Commands/Create/CreateWallCommand.cs | 66 ----- .../Commands/Create/CreateWallEventHandler.cs | 132 ---------- .../Commands/Delete/DeleteElementCommand.cs | 63 ----- .../Delete/DeleteElementEventHandler.cs | 98 ------- .../Commands/Test/SayHelloCommand.cs | 25 -- .../Commands/Test/SayHelloEventHandler.cs | 26 -- .../RevitApiCompatibilityExtensions.cs | 66 ----- SampleCommandSet/Models/FamilyTypeInfo.cs | 11 - SampleCommandSet/Models/Point.cs | 17 -- SampleCommandSet/Models/ViewInfo.cs | 22 -- SampleCommandSet/Models/WallInfo.cs | 21 -- SampleCommandSet/Properties/AssemblyInfo.cs | 36 --- SampleCommandSet/SampleCommandSet.csproj | 197 -------------- SampleCommandSet/command.json | 42 --- SampleCommandSet/packages.config | 6 - .../UI/CommandSetSettingsPage.xaml.cs | 6 +- revit-mcp-plugin/revit-mcp-plugin.csproj | 9 +- 26 files changed, 11 insertions(+), 1667 deletions(-) delete mode 100644 README_zh.md delete mode 100644 SampleCommandSet/Commands/Access/GetAvailableFamilyTypesCommand.cs delete mode 100644 SampleCommandSet/Commands/Access/GetAvailableFamilyTypesEventHandler.cs delete mode 100644 SampleCommandSet/Commands/Access/GetCurrentViewElementsCommand.cs delete mode 100644 SampleCommandSet/Commands/Access/GetCurrentViewElementsEventHandler.cs delete mode 100644 SampleCommandSet/Commands/Access/GetCurrentViewInfoCommand.cs delete mode 100644 SampleCommandSet/Commands/Access/GetCurrentViewInfoEventHandler.cs delete mode 100644 SampleCommandSet/Commands/Access/GetSelectedElementsCommand.cs delete mode 100644 SampleCommandSet/Commands/Access/GetSelectedElementsEventHandler.cs delete mode 100644 SampleCommandSet/Commands/Create/CreateWallCommand.cs delete mode 100644 SampleCommandSet/Commands/Create/CreateWallEventHandler.cs delete mode 100644 SampleCommandSet/Commands/Delete/DeleteElementCommand.cs delete mode 100644 SampleCommandSet/Commands/Delete/DeleteElementEventHandler.cs delete mode 100644 SampleCommandSet/Commands/Test/SayHelloCommand.cs delete mode 100644 SampleCommandSet/Commands/Test/SayHelloEventHandler.cs delete mode 100644 SampleCommandSet/Extensions/RevitApiCompatibilityExtensions.cs delete mode 100644 SampleCommandSet/Models/FamilyTypeInfo.cs delete mode 100644 SampleCommandSet/Models/Point.cs delete mode 100644 SampleCommandSet/Models/ViewInfo.cs delete mode 100644 SampleCommandSet/Models/WallInfo.cs delete mode 100644 SampleCommandSet/Properties/AssemblyInfo.cs delete mode 100644 SampleCommandSet/SampleCommandSet.csproj delete mode 100644 SampleCommandSet/command.json delete mode 100644 SampleCommandSet/packages.config diff --git a/README_zh.md b/README_zh.md deleted file mode 100644 index 8a953c2..0000000 --- a/README_zh.md +++ /dev/null @@ -1,131 +0,0 @@ -# revit-mcp-plugin - -[English](README.md) | 简体中文 - -## 简介 - -revit-mcp-plugin 是一个revit插件,基于 MCP 协议制作,从而使AI可以对 Revit 进行交互。 - -本项目是revit-mcp项目中的一部分(接收信息,装载功能集,操作revit),还需要配合[revit-mcp](https://github.com/revit-mcp/revit-mcp)(向AI提供tools)以及[revit-mcp-commandset](https://github.com/revit-mcp/revit-mcp-commandset)(具体的功能实现)使用。 - -## 环境要求 - -- revit 2019~2024 - -## 使用方法 - -### 注册插件 - -注册插件,重启Revit - -``` - - - - revit-mcp - %your_path%\revit-mcp-plugin.dll - revit_mcp_plugin.Core.Application - 090A4C8C-61DC-426D-87DF-E4BAE0F80EC1 - revit-mcp - https://github.com/revit-mcp/revit-mcp-plugin - - -``` - -`%your_path%`需要替换为实际编译后的路径 - -### 配置命令 - -附加模块->Revit MCP Plugin->Settings - -这个界面用于配置需要装载到revit中的命令,点击OpenCommandSetFolder打开存放命令集的文件夹,一个典型的命令集文件夹结构是这样的 - -``` -命令集名称/ -├── 2019/ # 兼容不同版本的执行文件 -├── 2020/ -├── 2021/ -├── 2022/ -├── 2023/ -├── 2024/ -└── command.json # 配置文件 -``` - -成功被识别的命令被勾选后,才会被加载和使用 - -### 启用服务 - -附加模块->Revit MCP Plugin->Revit MCP Switch - -打开服务,让ai可以发现你的revit程序,现在ai可以操控你的revit了! - -> 注意:如果启用服务后,修改了配置的命令,可能需要重启REVIT才能使配置生效,这与是否命令已注册相关 - -## 自定义命令 - -可以参考[revit-mcp-commandset](https://github.com/revit-mcp/revit-mcp-commandset)项目,开发自定义命令 - -## 项目文件组织结构 - -``` -revit-mcp-plugin/ -├── Configuration/ # 配置管理相关类 -│ ├── CommandConfig.cs # 命令配置 -│ ├── ConfigurationManager.cs # 配置管理器 -│ ├── DeveloperInfo.cs # 开发者信息 -│ ├── FrameworkConfig.cs # 框架配置 -│ └── ServiceSettings.cs # 服务设置 -│ -├── Core/ # 程序入口和核心功能 -│ ├── Application.cs # 应用程序入口点 -│ ├── CommandExecutor.cs # 命令执行器 -│ ├── CommandManager.cs # 命令管理器 -│ ├── ExternalEventManager.cs # 外部事件管理器 -│ ├── MCPServiceConnection.cs # MCP服务连接 -│ ├── RevitCommandRegistry.cs # Revit命令注册 -│ ├── Settings.cs # 应用程序设置 -│ └── SocketService.cs # Socket服务实现 -│ -├── Models/ # 数据模型 -│ └── ... # 各种数据模型类 -│ -├── UI/ # WPF窗体界面 -│ └── ... # 界面相关类 -│ -└── Utils/ # 工具类 - ├── Logger.cs # 日志工具 - └── PathManager.cs # 路径管理工具 -``` - -### Configuration 目录 -负责管理插件的各种配置信息: - -- CommandConfig.cs: 定义命令相关配置 -- ConfigurationManager.cs: 管理配置的加载、保存和访问 -- DeveloperInfo.cs: 存储开发者相关信息 -- FrameworkConfig.cs: 框架级别的配置设置 -- ServiceSettings.cs: 服务相关设置 - -### Core 目录 -包含插件的核心功能和入口点: - -- Application.cs: 应用程序入口点,负责初始化插件 -- CommandExecutor.cs: 负责执行Revit命令的核心组件 -- CommandManager.cs: 管理和调度插件中的各种命令 -- ExternalEventManager.cs: 管理Revit外部事件 -- MCPServiceConnection.cs: MCP服务连接 -- RevitCommandRegistry.cs: 注册和管理可用的Revit命令 -- Settings.cs: 触发显示设置界面 -- SocketService.cs: 实现与外部客户端的Socket通信 - -### Models 目录 -包含数据模型类,用于在系统各部分之间传递数据。 - -### UI 目录 -包含插件的用户界面相关组件,使用WPF框架实现。 - -### Utils 目录 -提供各种辅助工具: - -- Logger.cs: 日志记录工具,用于调试和错误追踪 -- PathManager.cs: 项目相关文件路径管理 diff --git a/SampleCommandSet/Commands/Access/GetAvailableFamilyTypesCommand.cs b/SampleCommandSet/Commands/Access/GetAvailableFamilyTypesCommand.cs deleted file mode 100644 index 76d7c22..0000000 --- a/SampleCommandSet/Commands/Access/GetAvailableFamilyTypesCommand.cs +++ /dev/null @@ -1,53 +0,0 @@ -using Autodesk.Revit.UI; -using Newtonsoft.Json.Linq; -using revit_mcp_sdk.API.Base; -using System; -using System.Collections.Generic; - -namespace SampleCommandSet.Commands.Access -{ - /// - /// 获取当前项目中可用族类型的命令 - /// - public class GetAvailableFamilyTypesCommand : ExternalEventCommandBase - { - private GetAvailableFamilyTypesEventHandler _handler => (GetAvailableFamilyTypesEventHandler)Handler; - - public override string CommandName => "get_available_family_types"; - - public GetAvailableFamilyTypesCommand(UIApplication uiApp) - : base(new GetAvailableFamilyTypesEventHandler(), uiApp) - { - } - - public override object Execute(JObject parameters, string requestId) - { - try - { - // 解析参数 - List categoryList = parameters?["categoryList"]?.ToObject>() ?? new List(); - string familyNameFilter = parameters?["familyNameFilter"]?.Value(); - int? limit = parameters?["limit"]?.Value(); - - // 设置查询参数 - _handler.CategoryList = categoryList; - _handler.FamilyNameFilter = familyNameFilter; - _handler.Limit = limit; - - // 触发外部事件并等待完成,最多等待15秒 - if (RaiseAndWaitForCompletion(15000)) - { - return _handler.ResultFamilyTypes; - } - else - { - throw new TimeoutException("获取可用族类型超时"); - } - } - catch (Exception ex) - { - throw new Exception($"获取可用族类型失败: {ex.Message}"); - } - } - } -} \ No newline at end of file diff --git a/SampleCommandSet/Commands/Access/GetAvailableFamilyTypesEventHandler.cs b/SampleCommandSet/Commands/Access/GetAvailableFamilyTypesEventHandler.cs deleted file mode 100644 index fea70e9..0000000 --- a/SampleCommandSet/Commands/Access/GetAvailableFamilyTypesEventHandler.cs +++ /dev/null @@ -1,137 +0,0 @@ -using Autodesk.Revit.DB; -using Autodesk.Revit.UI; -using revit_mcp_sdk.API.Interfaces; -using SampleCommandSet.Extensions; -using SampleCommandSet.Models; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; - -namespace SampleCommandSet.Commands.Access -{ - public class GetAvailableFamilyTypesEventHandler : IExternalEventHandler, IWaitableExternalEventHandler - { - // 执行结果 - public List ResultFamilyTypes { get; private set; } - - // 状态同步对象 - public bool TaskCompleted { get; private set; } - private readonly ManualResetEvent _resetEvent = new ManualResetEvent(false); - - // 过滤条件 - public List CategoryList { get; set; } - public string FamilyNameFilter { get; set; } - public int? Limit { get; set; } - - // 执行时间,略微比调用超时更短一些 - public bool WaitForCompletion(int timeoutMilliseconds = 12500) - { - return _resetEvent.WaitOne(timeoutMilliseconds); - } - - public void Execute(UIApplication app) - { - try - { - var doc = app.ActiveUIDocument.Document; - - // 获取所有族符号(可载入族) - var familySymbols = new FilteredElementCollector(doc) - .OfClass(typeof(FamilySymbol)) - .Cast(); - // 获取系统族类型(墙、楼板等) - var systemTypes = new List(); - systemTypes.AddRange(new FilteredElementCollector(doc).OfClass(typeof(WallType)).Cast()); - systemTypes.AddRange(new FilteredElementCollector(doc).OfClass(typeof(FloorType)).Cast()); - systemTypes.AddRange(new FilteredElementCollector(doc).OfClass(typeof(RoofType)).Cast()); - systemTypes.AddRange(new FilteredElementCollector(doc).OfClass(typeof(CurtainSystemType)).Cast()); - // 合并结果 - var allElements = familySymbols - .Cast() - .Concat(systemTypes) - .ToList(); - - IEnumerable filteredElements = allElements; - - // 类别过滤 - if (CategoryList != null && CategoryList.Any()) - { - var validCategoryIds = new List(); - foreach (var categoryName in CategoryList) - { - if (Enum.TryParse(categoryName, out BuiltInCategory bic)) - { - validCategoryIds.Add((int)bic); - } - } - - if (validCategoryIds.Any()) - { - filteredElements = filteredElements.Where(et => - { - var categoryId = et.Category?.Id.GetIdValue(); - return categoryId != null && validCategoryIds.Contains((int)categoryId.Value); - }); - } - } - - // 名称模糊匹配(同时匹配族名和类型名) - if (!string.IsNullOrEmpty(FamilyNameFilter)) - { - filteredElements = filteredElements.Where(et => - { - string familyName = (et is FamilySymbol fs) ? fs.FamilyName : et.get_Parameter( - BuiltInParameter.SYMBOL_FAMILY_NAME_PARAM)?.AsString() ?? ""; - - return (familyName?.IndexOf(FamilyNameFilter, StringComparison.OrdinalIgnoreCase) >= 0 || - et.Name.IndexOf(FamilyNameFilter, StringComparison.OrdinalIgnoreCase) >= 0); - }); - } - - // 限制返回数量 - if (Limit.HasValue && Limit.Value > 0) - { - filteredElements = filteredElements.Take(Limit.Value); - } - - // 转换为FamilyTypeInfo列表 - ResultFamilyTypes = filteredElements.Select(et => - { - string familyName; - if (et is FamilySymbol fs) - { - familyName = fs.FamilyName; - } - else - { - Parameter param = et.get_Parameter(BuiltInParameter.SYMBOL_FAMILY_NAME_PARAM); - familyName = param?.AsString() ?? et.GetType().Name.Replace("Type", ""); - } - return new FamilyTypeInfo - { - FamilyTypeId = et.Id.GetIdValue(), - UniqueId = et.UniqueId, - FamilyName = familyName, - TypeName = et.Name, - Category = et.Category?.Name - }; - }).ToList(); - } - catch (Exception ex) - { - TaskDialog.Show("Error", "获取族类型失败: " + ex.Message); - } - finally - { - TaskCompleted = true; - _resetEvent.Set(); - } - } - - public string GetName() - { - return "获取可用族类型"; - } - } -} \ No newline at end of file diff --git a/SampleCommandSet/Commands/Access/GetCurrentViewElementsCommand.cs b/SampleCommandSet/Commands/Access/GetCurrentViewElementsCommand.cs deleted file mode 100644 index 555b740..0000000 --- a/SampleCommandSet/Commands/Access/GetCurrentViewElementsCommand.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Autodesk.Revit.UI; -using Newtonsoft.Json.Linq; -using revit_mcp_sdk.API.Base; -using System; -using System.Collections.Generic; - -namespace SampleCommandSet.Commands.Access -{ - /// - /// 获取当前视图元素的命令 - /// - public class GetCurrentViewElementsCommand : ExternalEventCommandBase - { - private GetCurrentViewElementsEventHandler _handler => (GetCurrentViewElementsEventHandler)Handler; - - public override string CommandName => "get_current_view_elements"; - - public GetCurrentViewElementsCommand(UIApplication uiApp) - : base(new GetCurrentViewElementsEventHandler(), uiApp) - { - } - - public override object Execute(JObject parameters, string requestId) - { - try - { - // 解析参数 - List modelCategoryList = parameters?["modelCategoryList"]?.ToObject>() ?? new List(); - List annotationCategoryList = parameters?["annotationCategoryList"]?.ToObject>() ?? new List(); - bool includeHidden = parameters?["includeHidden"]?.Value() ?? false; - int limit = parameters?["limit"]?.Value() ?? 100; - - // 设置查询参数 - _handler.SetQueryParameters(modelCategoryList, annotationCategoryList, includeHidden, limit); - - // 触发外部事件并等待完成 - if (RaiseAndWaitForCompletion(60000)) // 60秒超时 - { - return _handler.ResultInfo; - } - else - { - throw new TimeoutException("获取视图元素超时"); - } - } - catch (Exception ex) - { - throw new Exception($"获取视图元素失败: {ex.Message}"); - } - } - } -} diff --git a/SampleCommandSet/Commands/Access/GetCurrentViewElementsEventHandler.cs b/SampleCommandSet/Commands/Access/GetCurrentViewElementsEventHandler.cs deleted file mode 100644 index 852c2cf..0000000 --- a/SampleCommandSet/Commands/Access/GetCurrentViewElementsEventHandler.cs +++ /dev/null @@ -1,248 +0,0 @@ -using Autodesk.Revit.DB; -using Autodesk.Revit.UI; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using SampleCommandSet.Extensions; -using revit_mcp_sdk.API.Interfaces; - -namespace SampleCommandSet.Commands.Access -{ - /// - /// 获取当前视图元素的事件处理器 - /// - public class GetCurrentViewElementsEventHandler : IExternalEventHandler, IWaitableExternalEventHandler - { - // 默认模型类别列表 - private readonly List _defaultModelCategories = new List - { - "OST_Walls", - "OST_Doors", - "OST_Windows", - "OST_Furniture", - "OST_Columns", - "OST_Floors", - "OST_Roofs", - "OST_Stairs", - "OST_StructuralFraming", - "OST_Ceilings", - "OST_MEPSpaces", - "OST_Rooms" - }; - // 默认注释类别列表 - private readonly List _defaultAnnotationCategories = new List - { - "OST_Dimensions", - "OST_TextNotes", - "OST_GenericAnnotation", - "OST_WallTags", - "OST_DoorTags", - "OST_WindowTags", - "OST_RoomTags", - "OST_AreaTags", - "OST_SpaceTags", - "OST_ViewportLabels", - "OST_TitleBlocks" - }; - - // 查询参数 - private List _modelCategoryList; - private List _annotationCategoryList; - private bool _includeHidden; - private int _limit; - - // 执行结果 - public ViewElementsResult ResultInfo { get; private set; } - - // 状态同步对象 - public bool TaskCompleted { get; private set; } - private readonly ManualResetEvent _resetEvent = new ManualResetEvent(false); - - // 设置查询参数 - public void SetQueryParameters(List modelCategoryList, List annotationCategoryList, bool includeHidden, int limit) - { - _modelCategoryList = modelCategoryList; - _annotationCategoryList = annotationCategoryList; - _includeHidden = includeHidden; - _limit = limit; - TaskCompleted = false; - _resetEvent.Reset(); - } - - // 实现IWaitableExternalEventHandler接口 - public bool WaitForCompletion(int timeoutMilliseconds = 10000) - { - return _resetEvent.WaitOne(timeoutMilliseconds); - } - - public void Execute(UIApplication app) - { - try - { - var uiDoc = app.ActiveUIDocument; - var doc = uiDoc.Document; - var activeView = doc.ActiveView; - - // 如果传入的类别列表为空,则使用默认列表 - List modelCategories = (_modelCategoryList == null || _modelCategoryList.Count == 0) - ? _defaultModelCategories - : _modelCategoryList; - - List annotationCategories = (_annotationCategoryList == null || _annotationCategoryList.Count == 0) - ? _defaultAnnotationCategories - : _annotationCategoryList; - - // 合并所有类别 - List allCategories = new List(); - allCategories.AddRange(modelCategories); - allCategories.AddRange(annotationCategories); - - // 获取当前视图中的所有元素 - var collector = new FilteredElementCollector(doc, activeView.Id) - .WhereElementIsNotElementType(); - - // 获取所有元素 - IList elements = collector.ToElements(); - - // 按类别筛选 - if (allCategories.Count > 0) - { - // 转换字符串类别为枚举 - List builtInCategories = new List(); - foreach (string categoryName in allCategories) - { - if (Enum.TryParse(categoryName, out BuiltInCategory category)) - { - builtInCategories.Add(category); - } - } - // 如果成功解析了类别,则使用类别过滤器 - if (builtInCategories.Count > 0) - { - ElementMulticategoryFilter categoryFilter = new ElementMulticategoryFilter(builtInCategories); - elements = new FilteredElementCollector(doc, activeView.Id) - .WhereElementIsNotElementType() - .WherePasses(categoryFilter) - .ToElements(); - } - } - - // 过滤隐藏的元素 - if (!_includeHidden) - { - elements = elements.Where(e => !e.IsHidden(activeView)).ToList(); - } - - // 限制返回数量 - if (_limit > 0 && elements.Count > _limit) - { - elements = elements.Take(_limit).ToList(); - } - - // 构建结果 - var elementInfos = elements.Select(e => new ElementInfo - { - Id = e.Id.GetIdValue(), - UniqueId = e.UniqueId, - Name = e.Name, - Category = e.Category?.Name ?? "unknow", - Properties = GetElementProperties(e) - }).ToList(); - - ResultInfo = new ViewElementsResult - { - ViewId = activeView.Id.GetIdValue(), - ViewName = activeView.Name, - TotalElementsInView = new FilteredElementCollector(doc, activeView.Id).GetElementCount(), - FilteredElementCount = elementInfos.Count, - Elements = elementInfos - }; - } - catch (Exception ex) - { - TaskDialog.Show("error", ex.Message); - } - finally - { - TaskCompleted = true; - _resetEvent.Set(); - } - } - - private Dictionary GetElementProperties(Element element) - { - var properties = new Dictionary(); - - // 添加通用属性 - properties.Add("ElementId", element.Id.GetIdValue().ToString()); - - if (element.Location != null) - { - if (element.Location is LocationPoint locationPoint) - { - var point = locationPoint.Point; - properties.Add("LocationX", point.X.ToString("F2")); - properties.Add("LocationY", point.Y.ToString("F2")); - properties.Add("LocationZ", point.Z.ToString("F2")); - } - else if (element.Location is LocationCurve locationCurve) - { - var curve = locationCurve.Curve; - properties.Add("Start", $"{curve.GetEndPoint(0).X:F2}, {curve.GetEndPoint(0).Y:F2}, {curve.GetEndPoint(0).Z:F2}"); - properties.Add("End", $"{curve.GetEndPoint(1).X:F2}, {curve.GetEndPoint(1).Y:F2}, {curve.GetEndPoint(1).Z:F2}"); - properties.Add("Length", curve.Length.ToString("F2")); - } - } - - // 获取常用参数值 - var commonParams = new[] { "Comments", "Mark", "Level", "Family", "Type" }; - foreach (var paramName in commonParams) - { - Parameter param = element.LookupParameter(paramName); - if (param != null && !param.IsReadOnly) - { - if (param.StorageType == StorageType.String) - properties.Add(paramName, param.AsString() ?? ""); - else if (param.StorageType == StorageType.Double) - properties.Add(paramName, param.AsDouble().ToString("F2")); - else if (param.StorageType == StorageType.Integer) - properties.Add(paramName, param.AsInteger().ToString()); - else if (param.StorageType == StorageType.ElementId) - properties.Add(paramName, param.AsElementId().GetIdValue().ToString()); - } - } - - return properties; - } - - public string GetName() - { - return "获取当前视图元素"; - } - } - - /// - /// 元素信息数据结构 - /// - public class ElementInfo - { - public long Id { get; set; } - public string UniqueId { get; set; } - public string Name { get; set; } - public string Category { get; set; } - public Dictionary Properties { get; set; } = new Dictionary(); - } - - /// - /// 视图元素结果数据结构 - /// - public class ViewElementsResult - { - public long ViewId { get; set; } - public string ViewName { get; set; } - public int TotalElementsInView { get; set; } - public int FilteredElementCount { get; set; } - public List Elements { get; set; } = new List(); - } -} diff --git a/SampleCommandSet/Commands/Access/GetCurrentViewInfoCommand.cs b/SampleCommandSet/Commands/Access/GetCurrentViewInfoCommand.cs deleted file mode 100644 index 46387f4..0000000 --- a/SampleCommandSet/Commands/Access/GetCurrentViewInfoCommand.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Autodesk.Revit.UI; -using Newtonsoft.Json.Linq; -using revit_mcp_sdk.API.Base; -using System; - -namespace SampleCommandSet.Commands.Access -{ - public class GetCurrentViewInfoCommand : ExternalEventCommandBase - { - private GetCurrentViewInfoEventHandler _handler => (GetCurrentViewInfoEventHandler)Handler; - - public override string CommandName => "get_current_view_info"; - - public GetCurrentViewInfoCommand(UIApplication uiApp) - : base(new GetCurrentViewInfoEventHandler(), uiApp) - { - } - - public override object Execute(JObject parameters, string requestId) - { - // 触发外部事件并等待完成 - if (RaiseAndWaitForCompletion(10000)) // 10秒超时 - { - return _handler.ResultInfo; - } - else - { - throw new TimeoutException("获取信息超时"); - } - } - } - -} diff --git a/SampleCommandSet/Commands/Access/GetCurrentViewInfoEventHandler.cs b/SampleCommandSet/Commands/Access/GetCurrentViewInfoEventHandler.cs deleted file mode 100644 index bbe5457..0000000 --- a/SampleCommandSet/Commands/Access/GetCurrentViewInfoEventHandler.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Autodesk.Revit.UI; -using revit_mcp_sdk.API.Interfaces; -using System; -using System.Threading; -using SampleCommandSet.Extensions; -using SampleCommandSet.Models; - -namespace SampleCommandSet.Commands.Access -{ - public class GetCurrentViewInfoEventHandler: IExternalEventHandler, IWaitableExternalEventHandler - { - // 执行结果 - public ViewInfo ResultInfo { get; private set; } - - // 状态同步对象 - public bool TaskCompleted { get; private set; } - private readonly ManualResetEvent _resetEvent = new ManualResetEvent(false); - - // 实现IWaitableExternalEventHandler接口 - public bool WaitForCompletion(int timeoutMilliseconds = 10000) - { - return _resetEvent.WaitOne(timeoutMilliseconds); - } - - public void Execute(UIApplication app) - { - try - { - var uiDoc = app.ActiveUIDocument; - var doc = uiDoc.Document; - var activeView = doc.ActiveView; - - ResultInfo = new ViewInfo - { - Id = activeView.Id.GetIdValue(), - UniqueId = activeView.UniqueId, - Name = activeView.Name, - ViewType = activeView.ViewType.ToString(), - IsTemplate = activeView.IsTemplate, - Scale = activeView.Scale, - DetailLevel = activeView.DetailLevel.ToString(), - }; - } - catch (Exception ex) - { - TaskDialog.Show("error", "获取信息失败"); - } - finally - { - TaskCompleted = true; - _resetEvent.Set(); - } - } - - public string GetName() - { - return "获取当前视图信息"; - } - } -} diff --git a/SampleCommandSet/Commands/Access/GetSelectedElementsCommand.cs b/SampleCommandSet/Commands/Access/GetSelectedElementsCommand.cs deleted file mode 100644 index 6b5f6bd..0000000 --- a/SampleCommandSet/Commands/Access/GetSelectedElementsCommand.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Autodesk.Revit.UI; -using Newtonsoft.Json.Linq; -using revit_mcp_sdk.API.Base; -using System; - -namespace SampleCommandSet.Commands.Access -{ - /// - /// 获取当前选中元素的命令 - /// - public class GetSelectedElementsCommand : ExternalEventCommandBase - { - private GetSelectedElementsEventHandler _handler => (GetSelectedElementsEventHandler)Handler; - - public override string CommandName => "get_selected_elements"; - - public GetSelectedElementsCommand(UIApplication uiApp) - : base(new GetSelectedElementsEventHandler(), uiApp) - { - } - - public override object Execute(JObject parameters, string requestId) - { - try - { - // 解析参数 - int? limit = parameters?["limit"]?.Value(); - - // 设置数量限制 - _handler.Limit = limit; - - // 触发外部事件并等待完成 - if (RaiseAndWaitForCompletion(15000)) - { - return _handler.ResultElements; - } - else - { - throw new TimeoutException("获取选中元素超时"); - } - } - catch (Exception ex) - { - throw new Exception($"获取选中元素失败: {ex.Message}"); - } - } - } -} \ No newline at end of file diff --git a/SampleCommandSet/Commands/Access/GetSelectedElementsEventHandler.cs b/SampleCommandSet/Commands/Access/GetSelectedElementsEventHandler.cs deleted file mode 100644 index 4fb6dd7..0000000 --- a/SampleCommandSet/Commands/Access/GetSelectedElementsEventHandler.cs +++ /dev/null @@ -1,73 +0,0 @@ -using Autodesk.Revit.DB; -using Autodesk.Revit.UI; -using revit_mcp_sdk.API.Interfaces; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using SampleCommandSet.Extensions; - -namespace SampleCommandSet.Commands.Access -{ - public class GetSelectedElementsEventHandler : IExternalEventHandler, IWaitableExternalEventHandler - { - // 执行结果 - public List ResultElements { get; private set; } - - // 状态同步对象 - public bool TaskCompleted { get; private set; } - private readonly ManualResetEvent _resetEvent = new ManualResetEvent(false); - - // 限制返回的元素数量 - public int? Limit { get; set; } - - // 实现IWaitableExternalEventHandler接口 - public bool WaitForCompletion(int timeoutMilliseconds = 10000) - { - return _resetEvent.WaitOne(timeoutMilliseconds); - } - - public void Execute(UIApplication app) - { - try - { - var uiDoc = app.ActiveUIDocument; - var doc = uiDoc.Document; - - // 获取当前选中的元素 - var selectedIds = uiDoc.Selection.GetElementIds(); - var selectedElements = selectedIds.Select(id => doc.GetElement(id)).ToList(); - - // 应用数量限制 - if (Limit.HasValue && Limit.Value > 0) - { - selectedElements = selectedElements.Take(Limit.Value).ToList(); - } - - // 转换为ElementInfo列表 - ResultElements = selectedElements.Select(element => new ElementInfo - { - Id = element.Id.GetIdValue(), - UniqueId = element.UniqueId, - Name = element.Name, - Category = element.Category?.Name - }).ToList(); - } - catch (Exception ex) - { - TaskDialog.Show("Error", "获取选中元素失败: " + ex.Message); - ResultElements = new List(); - } - finally - { - TaskCompleted = true; - _resetEvent.Set(); - } - } - - public string GetName() - { - return "获取选中元素"; - } - } -} \ No newline at end of file diff --git a/SampleCommandSet/Commands/Create/CreateWallCommand.cs b/SampleCommandSet/Commands/Create/CreateWallCommand.cs deleted file mode 100644 index d25b67a..0000000 --- a/SampleCommandSet/Commands/Create/CreateWallCommand.cs +++ /dev/null @@ -1,66 +0,0 @@ -using Autodesk.Revit.UI; -using revit_mcp_sdk.API.Base; -using System; -using Newtonsoft.Json.Linq; - -namespace SampleCommandSet.Commands.Create -{ - /// - /// 创建墙命令 - /// - public class CreateWallCommand : ExternalEventCommandBase - { - private CreateWallEventHandler _handler => (CreateWallEventHandler)Handler; - - /// - /// 命令名称 - /// - public override string CommandName => "create_Wall"; - - /// - /// 构造函数 - /// - /// Revit UIApplication - public CreateWallCommand(UIApplication uiApp) - : base(new CreateWallEventHandler(), uiApp) - { - } - - /// - /// 执行命令 - /// - /// JSON参数 - /// 请求ID - /// 命令执行结果 - public override object Execute(JObject parameters, string requestId) - { - try - { - // 解析墙参数 - double startX = parameters["startX"].Value(); - double startY = parameters["startY"].Value(); - double endX = parameters["endX"].Value(); - double endY = parameters["endY"].Value(); - double height = parameters["height"].Value(); - double thickness = parameters["thickness"].Value(); - - // 设置墙体参数 - _handler.SetWallParameters(startX, startY, endX, endY, height, thickness); - - // 触发外部事件并等待完成 - if (RaiseAndWaitForCompletion(10000)) - { - return _handler.CreatedWallInfo; - } - else - { - throw new TimeoutException("创建墙体操作超时"); - } - } - catch (Exception ex) - { - throw new Exception($"创建墙体失败: {ex.Message}"); - } - } - } -} diff --git a/SampleCommandSet/Commands/Create/CreateWallEventHandler.cs b/SampleCommandSet/Commands/Create/CreateWallEventHandler.cs deleted file mode 100644 index e81fb20..0000000 --- a/SampleCommandSet/Commands/Create/CreateWallEventHandler.cs +++ /dev/null @@ -1,132 +0,0 @@ -using Autodesk.Revit.DB; -using Autodesk.Revit.UI; -using revit_mcp_sdk.API.Interfaces; -using SampleCommandSet.Models; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace SampleCommandSet.Commands.Create -{ - /// - /// 创建墙的外部事件处理器 - /// - public class CreateWallEventHandler : IExternalEventHandler, IWaitableExternalEventHandler - { - // 创建墙的参数 - private double _startX; - private double _startY; - private double _endX; - private double _endY; - private double _height; - private double _thickness; - - // 创建的墙体信息 - private Wall _createdWall; - public WallInfo CreatedWallInfo { get; private set; } - - // 标记操作是否完成 - private bool _taskCompleted; - - // 事件等待对象 - private readonly ManualResetEvent _resetEvent = new ManualResetEvent(false); - - /// - /// 设置创建墙的参数 - /// - public void SetWallParameters(double startX, double startY, double endX, double endY, double height, double thickness) - { - _startX = startX; - _startY = startY; - _endX = endX; - _endY = endY; - _height = height; - _thickness = thickness; - - _taskCompleted = false; - _resetEvent.Reset(); - } - - /// - /// 等待墙创建完成 - /// - /// 超时时间(毫秒) - /// 操作是否在超时前完成 - public bool WaitForCompletion(int timeoutMilliseconds = 10000) - { - return _resetEvent.WaitOne(timeoutMilliseconds); - } - - /// - /// IExternalEventHandler.Execute 实现 - /// - public void Execute(UIApplication app) - { - try - { - Document doc = app.ActiveUIDocument.Document; - - using (Transaction trans = new Transaction(doc, "创建墙体")) - { - trans.Start(); - - // 创建墙的起点和终点 - XYZ startPoint = new XYZ(_startX, _startY, 0); - XYZ endPoint = new XYZ(_endX, _endY, 0); - - // 创建墙的曲线 - Line curve = Line.CreateBound(startPoint, endPoint); - - // 获取当前文档中的墙类型 - FilteredElementCollector collector = new FilteredElementCollector(doc); - collector.OfClass(typeof(WallType)); - WallType wallType = collector.FirstOrDefault(w => w.Name.Contains("常规")) as WallType; - - // 创建墙 - _createdWall = Wall.Create( - doc, - curve, - wallType.Id, - doc.ActiveView.GenLevel.Id, - _height, - 0.0, // 墙基点偏移 - false, // 不翻转 - false); // 不是结构墙 - - trans.Commit(); - - // 获取墙的详细信息 - CreatedWallInfo = new WallInfo - { - ElementId = _createdWall.Id.IntegerValue, - StartPoint = new Models.Point { X = startPoint.X, Y = startPoint.Y, Z = 0 }, - EndPoint = new Models.Point { X = endPoint.X, Y = endPoint.Y, Z = 0 }, - Height = _height, - Thickness = _thickness, - }; - } - } - catch (Exception ex) - { - TaskDialog.Show("错误", $"创建墙体时出错: {ex.Message}"); - - } - finally - { - _taskCompleted = true; - _resetEvent.Set(); // 通知等待线程操作已完成 - } - } - - /// - /// IExternalEventHandler.GetName 实现 - /// - public string GetName() - { - return "创建墙体"; - } - } -} diff --git a/SampleCommandSet/Commands/Delete/DeleteElementCommand.cs b/SampleCommandSet/Commands/Delete/DeleteElementCommand.cs deleted file mode 100644 index 3332303..0000000 --- a/SampleCommandSet/Commands/Delete/DeleteElementCommand.cs +++ /dev/null @@ -1,63 +0,0 @@ -using Autodesk.Revit.UI; -using Newtonsoft.Json.Linq; -using revit_mcp_sdk.API.Base; -using revit_mcp_sdk.API.Models; -using revit_mcp_sdk.Exceptions; -using System; - -namespace SampleCommandSet.Commands.Delete -{ - public class DeleteElementCommand : ExternalEventCommandBase - { - private DeleteElementEventHandler _handler => (DeleteElementEventHandler)Handler; - public override string CommandName => "delete_element"; - public DeleteElementCommand(UIApplication uiApp) - : base(new DeleteElementEventHandler(), uiApp) - { - } - public override object Execute(JObject parameters, string requestId) - { - try - { - // 解析数组参数 - var elementIds = parameters?["elementIds"]?.ToObject(); - if (elementIds == null || elementIds.Length == 0) - { - throw new CommandExecutionException( - "元素ID列表不能为空", - JsonRPCErrorCodes.InvalidParams); - } - // 设置要删除的元素ID数组 - _handler.ElementIds = elementIds; - // 触发外部事件并等待完成 - if (RaiseAndWaitForCompletion(15000)) - { - if (_handler.IsSuccess) - { - return CommandResult.CreateSuccess(new { deleted = true, count = _handler.DeletedCount }); - } - else - { - throw new CommandExecutionException( - "删除元素失败", - JsonRPCErrorCodes.ElementDeletionFailed); - } - } - else - { - throw CreateTimeoutException(CommandName); - } - } - catch (CommandExecutionException) - { - throw; - } - catch (Exception ex) - { - throw new CommandExecutionException( - $"删除元素失败: {ex.Message}", - JsonRPCErrorCodes.InternalError); - } - } - } -} diff --git a/SampleCommandSet/Commands/Delete/DeleteElementEventHandler.cs b/SampleCommandSet/Commands/Delete/DeleteElementEventHandler.cs deleted file mode 100644 index 964959e..0000000 --- a/SampleCommandSet/Commands/Delete/DeleteElementEventHandler.cs +++ /dev/null @@ -1,98 +0,0 @@ -using Autodesk.Revit.DB; -using Autodesk.Revit.UI; -using revit_mcp_sdk.API.Interfaces; -using System; -using System.Collections.Generic; -using System.Threading; - -namespace SampleCommandSet.Commands.Delete -{ - public class DeleteElementEventHandler : IExternalEventHandler, IWaitableExternalEventHandler - { - // 执行结果 - public bool IsSuccess { get; private set; } - - // 成功删除的元素数量 - public int DeletedCount { get; private set; } - // 状态同步对象 - public bool TaskCompleted { get; private set; } - private readonly ManualResetEvent _resetEvent = new ManualResetEvent(false); - // 要删除的元素ID数组 - public string[] ElementIds { get; set; } - // 实现IWaitableExternalEventHandler接口 - public bool WaitForCompletion(int timeoutMilliseconds = 10000) - { - return _resetEvent.WaitOne(timeoutMilliseconds); - } - public void Execute(UIApplication app) - { - try - { - var doc = app.ActiveUIDocument.Document; - DeletedCount = 0; - if (ElementIds == null || ElementIds.Length == 0) - { - IsSuccess = false; - return; - } - // 创建待删除元素ID集合 - List elementIdsToDelete = new List(); - List invalidIds = new List(); - foreach (var idStr in ElementIds) - { - if (int.TryParse(idStr, out int elementIdValue)) - { - var elementId = new ElementId(elementIdValue); - // 检查元素是否存在 - if (doc.GetElement(elementId) != null) - { - elementIdsToDelete.Add(elementId); - } - } - else - { - invalidIds.Add(idStr); - } - } - if (invalidIds.Count > 0) - { - TaskDialog.Show("警告", $"以下ID无效或元素不存在:{string.Join(", ", invalidIds)}"); - } - // 如果有可删除的元素,则执行删除 - if (elementIdsToDelete.Count > 0) - { - using (var transaction = new Transaction(doc, "Delete Elements")) - { - transaction.Start(); - - // 批量删除元素 - ICollection deletedIds = doc.Delete(elementIdsToDelete); - DeletedCount = deletedIds.Count; - - transaction.Commit(); - } - IsSuccess = true; - } - else - { - TaskDialog.Show("错误", "没有有效的元素可以删除"); - IsSuccess = false; - } - } - catch (Exception ex) - { - TaskDialog.Show("错误", "删除元素失败: " + ex.Message); - IsSuccess = false; - } - finally - { - TaskCompleted = true; - _resetEvent.Set(); - } - } - public string GetName() - { - return "删除元素"; - } - } -} diff --git a/SampleCommandSet/Commands/Test/SayHelloCommand.cs b/SampleCommandSet/Commands/Test/SayHelloCommand.cs deleted file mode 100644 index 490fd08..0000000 --- a/SampleCommandSet/Commands/Test/SayHelloCommand.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Autodesk.Revit.UI; -using Newtonsoft.Json.Linq; -using revit_mcp_sdk.API.Base; -using revit_mcp_sdk.API.Models; - -namespace SampleCommandSet.Commands.Test -{ - public class SayHelloCommand : ExternalEventCommandBase - { - private SayHelloEventHandler _handler => (SayHelloEventHandler)Handler; - - public override string CommandName => "say_hello"; - - public SayHelloCommand(UIApplication uiApp) - : base(new SayHelloEventHandler(), uiApp) - { - } - - public override object Execute(JObject parameters, string requestId) - { - RaiseAndWaitForCompletion(15000); - return CommandResult.CreateSuccess(new { execute = true }); - } - } -} diff --git a/SampleCommandSet/Commands/Test/SayHelloEventHandler.cs b/SampleCommandSet/Commands/Test/SayHelloEventHandler.cs deleted file mode 100644 index f6bc3ce..0000000 --- a/SampleCommandSet/Commands/Test/SayHelloEventHandler.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Autodesk.Revit.UI; -using revit_mcp_sdk.API.Interfaces; -using System.Threading; - -namespace SampleCommandSet.Commands.Test -{ - public class SayHelloEventHandler : IExternalEventHandler, IWaitableExternalEventHandler - { - private readonly ManualResetEvent _resetEvent = new ManualResetEvent(false); - - public bool WaitForCompletion(int timeoutMilliseconds = 10000) - { - return _resetEvent.WaitOne(timeoutMilliseconds); - } - - public void Execute(UIApplication app) - { - TaskDialog.Show("revit-mcp", "hello MCP"); - } - - public string GetName() - { - return "say hello"; - } - } -} diff --git a/SampleCommandSet/Extensions/RevitApiCompatibilityExtensions.cs b/SampleCommandSet/Extensions/RevitApiCompatibilityExtensions.cs deleted file mode 100644 index 1a21fe7..0000000 --- a/SampleCommandSet/Extensions/RevitApiCompatibilityExtensions.cs +++ /dev/null @@ -1,66 +0,0 @@ -using Autodesk.Revit.DB; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; - -namespace SampleCommandSet.Extensions -{ - /// - /// 提供 Revit API 跨版本兼容性的扩展方法 - /// - public static class RevitApiCompatibilityExtensions - { - // 缓存反射结果以提高性能 - private static readonly Lazy ElementIdValueProperty = - new Lazy(() => typeof(ElementId).GetProperty("Value")); - - private static readonly Lazy ElementIdIntegerValueProperty = - new Lazy(() => typeof(ElementId).GetProperty("IntegerValue")); - - /// - /// 以版本兼容的方式获取 ElementId 的整数值 - /// - public static int GetIdValue(this ElementId id) - { - if (id == null) - throw new ArgumentNullException(nameof(id)); - - // 先检查是否有 Value 属性 (Revit 2022+) - if (ElementIdValueProperty.Value != null) - { - try - { - return (int)ElementIdValueProperty.Value.GetValue(id); - } - catch - { - // 失败时回退到 IntegerValue - } - } - - // 使用 IntegerValue (旧版本 Revit) - return id.IntegerValue; - } - - /// - /// 获取文档当前 Revit 版本号 - /// - public static int GetRevitVersionNumber(this Document doc) - { - if (doc == null) - throw new ArgumentNullException(nameof(doc)); - - string versionString = doc.Application.VersionNumber; - - if (int.TryParse(versionString, out int versionNumber)) - { - return versionNumber; - } - return 0; - } - - } -} diff --git a/SampleCommandSet/Models/FamilyTypeInfo.cs b/SampleCommandSet/Models/FamilyTypeInfo.cs deleted file mode 100644 index 67a2352..0000000 --- a/SampleCommandSet/Models/FamilyTypeInfo.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace SampleCommandSet.Models -{ - public class FamilyTypeInfo - { - public long FamilyTypeId { get; set; } - public string UniqueId { get; set; } - public string FamilyName { get; set; } - public string TypeName { get; set; } - public string Category { get; set; } - } -} diff --git a/SampleCommandSet/Models/Point.cs b/SampleCommandSet/Models/Point.cs deleted file mode 100644 index a6720ec..0000000 --- a/SampleCommandSet/Models/Point.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Newtonsoft.Json; - -namespace SampleCommandSet.Models -{ - /// - /// 点坐标 - /// - public class Point - { - [JsonProperty("x")] - public double X { get; set; } - [JsonProperty("y")] - public double Y { get; set; } - [JsonProperty("z")] - public double Z { get; set; } - } -} diff --git a/SampleCommandSet/Models/ViewInfo.cs b/SampleCommandSet/Models/ViewInfo.cs deleted file mode 100644 index a177d21..0000000 --- a/SampleCommandSet/Models/ViewInfo.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace SampleCommandSet.Models -{ - /// - /// 视图信息数据结构 - /// - public class ViewInfo - { - public long Id { get; set; } - public string UniqueId { get; set; } - public string Name { get; set; } - public string ViewType { get; set; } - public bool IsTemplate { get; set; } - public int Scale { get; set; } - public string DetailLevel { get; set; } - } -} diff --git a/SampleCommandSet/Models/WallInfo.cs b/SampleCommandSet/Models/WallInfo.cs deleted file mode 100644 index 6ae3fc4..0000000 --- a/SampleCommandSet/Models/WallInfo.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Newtonsoft.Json; - -namespace SampleCommandSet.Models -{ - /// - /// 墙体信息结构,用于返回创建的墙的详细信息 - /// - public class WallInfo - { - [JsonProperty("elementId")] - public int ElementId { get; set; } - [JsonProperty("startPoint")] - public Point StartPoint { get; set; } = new Point(); - [JsonProperty("endPoint")] - public Point EndPoint { get; set; } = new Point(); - [JsonProperty("height")] - public double Height { get; set; } - [JsonProperty("thickness")] - public double Thickness { get; set; } - } -} diff --git a/SampleCommandSet/Properties/AssemblyInfo.cs b/SampleCommandSet/Properties/AssemblyInfo.cs deleted file mode 100644 index ea88862..0000000 --- a/SampleCommandSet/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// 有关程序集的一般信息由以下 -// 控制。更改这些特性值可修改 -// 与程序集关联的信息。 -[assembly: AssemblyTitle("SampleCommandSet")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("P R C")] -[assembly: AssemblyProduct("SampleCommandSet")] -[assembly: AssemblyCopyright("Copyright © P R C 2025")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// 将 ComVisible 设置为 false 会使此程序集中的类型 -//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 -//请将此类型的 ComVisible 特性设置为 true。 -[assembly: ComVisible(false)] - -// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID -[assembly: Guid("1080b354-f092-49b9-a708-53e971d6ea95")] - -// 程序集的版本信息由下列四个值组成: -// -// 主版本 -// 次版本 -// 生成号 -// 修订号 -// -//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 -//通过使用 "*",如下所示: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/SampleCommandSet/SampleCommandSet.csproj b/SampleCommandSet/SampleCommandSet.csproj deleted file mode 100644 index d270033..0000000 --- a/SampleCommandSet/SampleCommandSet.csproj +++ /dev/null @@ -1,197 +0,0 @@ - - - - - Debug - AnyCPU - {1080B354-F092-49B9-A708-53E971D6EA95} - Library - Properties - SampleCommandSet - SampleCommandSet - v4.8 - 512 - true - - - true - ..\revit-mcp-plugin\bin\x64\Debug\commands\SampleCommandset\2019\ - DEBUG;TRACE - full - x64 - 7.3 - prompt - - - ..\revit-mcp-plugin\bin\x64\Release\commands\SampleCommandset\2019\ - TRACE - true - pdbonly - x64 - 7.3 - prompt - - - true - ..\revit-mcp-plugin\bin\x64\Debug\commands\SampleCommandset\2020\ - DEBUG;TRACE - full - x64 - 7.3 - prompt - - - ..\revit-mcp-plugin\bin\x64\Release\commands\SampleCommandset\2020\ - TRACE - true - pdbonly - x64 - 7.3 - prompt - - - true - ..\revit-mcp-plugin\bin\x64\Debug\commands\SampleCommandset\2021\ - DEBUG;TRACE - full - x64 - 7.3 - prompt - - - ..\revit-mcp-plugin\bin\x64\Release\commands\SampleCommandset\2021\ - TRACE - true - pdbonly - x64 - 7.3 - prompt - - - true - ..\revit-mcp-plugin\bin\x64\Debug\commands\SampleCommandset\2022\ - DEBUG;TRACE - full - x64 - 7.3 - prompt - - - ..\revit-mcp-plugin\bin\x64\Release\commands\SampleCommandset\2022\ - TRACE - true - pdbonly - x64 - 7.3 - prompt - - - true - ..\revit-mcp-plugin\bin\x64\Debug\commands\SampleCommandset\2023\ - DEBUG;TRACE - full - x64 - 7.3 - prompt - - - ..\revit-mcp-plugin\bin\x64\Release\commands\SampleCommandset\2023\ - TRACE - true - pdbonly - x64 - 7.3 - prompt - - - ..\revit-mcp-plugin\bin\x64\Debug\commands\SampleCommandset\2024\ - TRACE - true - pdbonly - ARM64 - 7.3 - prompt - - - ..\revit-mcp-plugin\bin\x64\Release\commands\SampleCommandset\2024\ - TRACE - true - pdbonly - x64 - 7.3 - prompt - - - - ..\packages\Revit_API_x64.2024.0.2\lib\NET480\AdWindows.dll - False - - - ..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll - - - ..\packages\revit-mcp-sdk.1.0.0-beta.1\lib\net48\revit-mcp-sdk.dll - - - ..\packages\Revit_API_x64.2024.0.2\lib\NET480\RevitAddInUtility.dll - False - - - ..\packages\Revit_API_x64.2024.0.2\lib\NET480\RevitAPI.dll - False - - - ..\packages\Revit_API_x64.2024.0.2\lib\NET480\RevitAPIUI.dll - False - - - - - - - - - - - ..\packages\Revit_API_x64.2024.0.2\lib\NET480\UIFramework.dll - False - - - ..\packages\Revit_API_x64.2024.0.2\lib\NET480\UIFrameworkServices.dll - False - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/SampleCommandSet/command.json b/SampleCommandSet/command.json deleted file mode 100644 index c9ebbed..0000000 --- a/SampleCommandSet/command.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "name": "SampleCommandSet", - "description": "Basic command collection for Revit AI assistance", - "developer": { - "name": "revit-mcp", - "email": "", - "website": "", - "organization": "revit-mcp" - }, - "commands": [ - { - "commandName": "say_hello", - "description": "Displays a greeting dialog", - "assemblyPath": "SampleCommandSet.dll" - }, - { - "commandName": "get_available_family_types", - "description": "get available family types", - "assemblyPath": "SampleCommandSet.dll" - }, - { - "commandName": "get_current_view_elements", - "description": "get current view elements", - "assemblyPath": "SampleCommandSet.dll" - }, - { - "commandName": "get_current_view_info", - "description": "get current view info", - "assemblyPath": "SampleCommandSet.dll" - }, - { - "commandName": "create_Wall", - "description": "create a wall", - "assemblyPath": "SampleCommandSet.dll" - }, - { - "commandName": "delete_element", - "description": "Deletes elements using ElementId", - "assemblyPath": "SampleCommandSet.dll" - } - ] -} \ No newline at end of file diff --git a/SampleCommandSet/packages.config b/SampleCommandSet/packages.config deleted file mode 100644 index a26c285..0000000 --- a/SampleCommandSet/packages.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/revit-mcp-plugin/UI/CommandSetSettingsPage.xaml.cs b/revit-mcp-plugin/UI/CommandSetSettingsPage.xaml.cs index 10d76eb..e949abf 100644 --- a/revit-mcp-plugin/UI/CommandSetSettingsPage.xaml.cs +++ b/revit-mcp-plugin/UI/CommandSetSettingsPage.xaml.cs @@ -121,14 +121,14 @@ private void LoadCommandSets() // 如果至少有一个版本支持此命令 if (supportedCommandVersions.Count > 0 && dllBasePath != null) { - // 创建命令配置 + // 创建命令配置 - 默认启用命令 var commandConfig = new CommandConfig { CommandName = command.CommandName, Description = command.Description, // 使用带有版本占位符的路径 AssemblyPath = dllBasePath, - Enabled = false, + Enabled = true, // 默认启用所有找到的命令 // 记录所有支持的版本 SupportedRevitVersions = supportedCommandVersions.ToArray() }; @@ -406,4 +406,4 @@ public class CommandItemJson public string Description { get; set; } public string AssemblyPath { get; set; } } -} \ No newline at end of file +} diff --git a/revit-mcp-plugin/revit-mcp-plugin.csproj b/revit-mcp-plugin/revit-mcp-plugin.csproj index 4b28c0b..e7af3ef 100644 --- a/revit-mcp-plugin/revit-mcp-plugin.csproj +++ b/revit-mcp-plugin/revit-mcp-plugin.csproj @@ -76,16 +76,23 @@ + + bin\AddIn $(RevitVersion) $(Configuration)\ $(RootDir)$(RootNameSpace)\ + $(AddinDir)Commands\ + + + + @@ -93,4 +100,4 @@ - \ No newline at end of file + From e11702fda6a3f4e7ec603ad8e1dd646d581c4d0f Mon Sep 17 00:00:00 2001 From: Neel Shah Date: Tue, 21 Oct 2025 16:20:25 -0700 Subject: [PATCH 2/7] docs: comprehensive README improvements - Add modern formatting with badges and emojis - Restructure with table of contents and clear sections - Add comprehensive installation and configuration guides - Include troubleshooting section with common issues - Add contributing guidelines and development setup - Improve visual appeal and professional presentation - Fix grammatical issues and improve clarity - Add prerequisites and system requirements - Include better examples and practical information --- README.md | 342 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 245 insertions(+), 97 deletions(-) diff --git a/README.md b/README.md index 20070cf..398bf11 100644 --- a/README.md +++ b/README.md @@ -1,131 +1,279 @@ -# revit-mcp-plugin +# 🔗 Revit MCP Plugin + +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +[![Revit 2019-2024](https://img.shields.io/badge/Revit-2019--2024-blue.svg)](https://www.autodesk.com/products/revit/overview) +[![Platform: Windows](https://img.shields.io/badge/Platform-Windows-lightgrey.svg)](https://www.microsoft.com/windows) + +> A powerful Revit plugin that enables AI assistants to interact with Autodesk Revit through the Model Context Protocol (MCP) + +## 📋 Table of Contents + +- [✨ Features](#-features) +- [📋 Prerequisites](#-prerequisites) +- [🚀 Installation](#-installation) +- [⚙️ Configuration](#️-configuration) +- [🎯 Usage](#-usage) +- [🔧 Custom Commands](#-custom-commands) +- [🏗️ Project Structure](#️-project-structure) +- [🐛 Troubleshooting](#-troubleshooting) +- [🤝 Contributing](#-contributing) +- [📄 License](#-license) + +## ✨ Features + +- **🤖 AI Integration**: Seamless integration with AI assistants via MCP protocol +- **⚡ Command Management**: Dynamic loading and management of Revit command sets +- **🔌 Socket Communication**: Real-time communication with external MCP services +- **🎛️ Settings Interface**: User-friendly configuration through Revit add-in interface +- **📦 Version Compatibility**: Support for Revit 2019-2024 +- **🔧 Extensible Architecture**: Easy development of custom commands + +## 📋 Prerequisites + +### System Requirements +- **Operating System**: Windows 10/11 (64-bit) +- **Revit Version**: 2019, 2020, 2021, 2022, 2023, or 2024 +- **.NET Framework**: 4.8 or higher +- **Memory**: 8GB RAM minimum, 16GB recommended +- **Disk Space**: 500MB free space + +### Dependencies +This plugin works in conjunction with: +- **[revit-mcp](https://github.com/revit-mcp/revit-mcp)**: Core MCP service provider +- **[revit-mcp-commandset](https://github.com/revit-mcp/revit-mcp-commandset)**: Command implementations + +## 🚀 Installation + +### Step 1: Build the Plugin + +1. **Clone the repository**: + ```bash + git clone https://github.com/neelshah-starline/revit-mcp-plugin.git + cd revit-mcp-plugin + ``` + +2. **Build the solution**: + - Open `revit-mcp-plugin.sln` in Visual Studio + - Set build configuration to `Release` + - Build the solution (`Ctrl+Shift+B`) + +3. **Locate the compiled DLL**: + - Navigate to `revit-mcp-plugin\bin\Release\` + - Copy `revit-mcp-plugin.dll` to your desired location + +### Step 2: Register the Plugin + +1. **Create the add-in file** (`revit-mcp-plugin.addin`): + ```xml + + + + revit-mcp + %your_path%\revit-mcp-plugin.dll + revit_mcp_plugin.Core.Application + 090A4C8C-61DC-426D-87DF-E4BAE0F80EC1 + revit-mcp + https://github.com/neelshah-starline/revit-mcp-plugin + + + ``` + +2. **Install the add-in**: + - Copy the `.addin` file to one of these locations: + - **Current User**: `%APPDATA%\Autodesk\Revit\Addins\2024\` (or your Revit version) + - **All Users**: `%ALLUSERSPROFILE%\Autodesk\Revit\Addins\2024\` (or your Revit version) + - Replace `%your_path%` with the actual path to your compiled DLL + +3. **Restart Revit** to load the plugin + +## ⚙️ Configuration + +### Initial Setup + +1. **Access Settings**: + - Launch Revit + - Navigate to **Add-in Modules → Revit MCP Plugin → Settings** + +2. **Configure Command Sets**: + - Click **OpenCommandSetFolder** to open the command sets directory + - Place your command set folders in this location + +### Command Set Structure -English | [简体中文](README_zh.md) +A typical command set should follow this structure: -## Introduction +``` +MyCommandSet/ +├── 2019/ # Revit 2019 compatible DLL +├── 2020/ # Revit 2020 compatible DLL +├── 2021/ # Revit 2021 compatible DLL +├── 2022/ # Revit 2022 compatible DLL +├── 2023/ # Revit 2023 compatible DLL +├── 2024/ # Revit 2024 compatible DLL +└── command.json # Command configuration file +``` -revit-mcp-plugin is a Revit plugin based on the MCP protocol, enabling AI to interact with Revit. +3. **Enable Commands**: + - Check the boxes for commands you want to load + - Click **Save** to apply changes -This project is part of the revit-mcp project (receives messages, loads command sets, operates Revit), and needs to be used in conjunction with [revit-mcp](https://github.com/revit-mcp/revit-mcp) (provides tools to AI) and [revit-mcp-commandset](https://github.com/revit-mcp/revit-mcp-commandset) (specific feature implementations). +## 🎯 Usage -## Environment Requirements +### Enable MCP Service -- Revit 2019~2024 +1. **Start the Service**: + - Go to **Add-in → Revit MCP Plugin → Revit MCP Switch** + - Toggle the service **ON** -## Usage Instructions +2. **AI Integration**: + - Your AI assistant can now discover and control your Revit instance + - The plugin will appear as an available MCP server -### Register Plugin +### Important Notes -Register the plugin and restart Revit: +- **⚠️ Configuration Changes**: If you modify command configurations after enabling the service, restart Revit for changes to take effect +- **🔒 Service State**: The service state persists between Revit sessions +- **📊 Real-time Updates**: Command availability updates dynamically when service is active -```xml - - - - revit-mcp - %your_path%\revit-mcp-plugin.dll - revit_mcp_plugin.Core.Application - 090A4C8C-61DC-426D-87DF-E4BAE0F80EC1 - revit-mcp - https://github.com/revit-mcp/revit-mcp-plugin - - -``` +## 🔧 Custom Commands -`%your_path%` needs to be replaced with the actual path after compilation. +### Development Overview -### Configure Commands +Custom commands extend the plugin's functionality. Refer to the [revit-mcp-commandset](https://github.com/revit-mcp/revit-mcp-commandset) project for detailed development guidelines. -Add-in Modules -> Revit MCP Plugin -> Settings +### Quick Start -This interface is used to configure the commands to be loaded into Revit. Click OpenCommandSetFolder to open the folder storing command sets. A typical command set folder structure looks like this: +1. **Create Command Class**: + ```csharp + public class MyCustomCommand : RevitCommandBase + { + public override void Execute(ExternalCommandData commandData) + { + // Your command implementation + } + } + ``` + +2. **Register Command**: + - Implement `IExternalCommand` interface + - Add command configuration to `command.json` + - Build for target Revit versions + +## 🏗️ Project Structure ``` -CommandSetName/ -├── 2019/ # Compatible executable files for different versions -├── 2020/ -├── 2021/ -├── 2022/ -├── 2023/ -├── 2024/ -└── command.json # Configuration file +revit-mcp-plugin/ +├── Configuration/ # Configuration management +│ ├── CommandConfig.cs # Command configuration models +│ ├── ConfigurationManager.cs # Configuration loading/saving +│ ├── DeveloperInfo.cs # Developer information +│ ├── FrameworkConfig.cs # Framework settings +│ └── ServiceSettings.cs # Service configuration +│ +├── Core/ # Core functionality +│ ├── Application.cs # Plugin entry point +│ ├── CommandExecutor.cs # Command execution engine +│ ├── CommandManager.cs # Command lifecycle management +│ ├── ExternalEventManager.cs # Revit external events +│ ├── MCPServiceConnection.cs # MCP protocol handling +│ ├── RevitCommandRegistry.cs # Command registration +│ ├── Settings.cs # Settings interface trigger +│ └── SocketService.cs # Network communication +│ +├── UI/ # User interface components +│ ├── SettingsWindow.xaml # Main settings window +│ ├── SettingsWindow.xaml.cs # Settings window logic +│ └── CommandSetSettingsPage.xaml # Command configuration UI +│ +└── Utils/ # Utility classes + ├── Logger.cs # Logging functionality + └── PathManager.cs # Path management utilities ``` -Successfully identified commands need to be checked to be loaded and used. +## 🐛 Troubleshooting -### Enable Service +### Common Issues -Add-in -> Revit MCP Plugin -> Revit MCP Switch +**❌ Plugin doesn't load** +- Verify the `.addin` file is in the correct location +- Check that the DLL path in the `.addin` file is correct +- Ensure Revit version compatibility +- Check Windows Event Viewer for error details -Open the service to allow AI to discover your Revit program. Now AI can control your Revit! +**❌ Commands don't appear** +- Verify command set folder structure +- Check `command.json` syntax +- Ensure compatible DLLs are present for your Revit version +- Restart Revit after configuration changes -> Note: If you modify the configured commands after enabling the service, you may need to restart REVIT for the configuration to take effect. This is related to whether the command has already been registered. +**❌ MCP service not discovered** +- Confirm the service is enabled in Revit +- Check firewall settings for network communication +- Verify MCP client configuration -## Custom Commands +**❌ Command execution fails** +- Check Revit API compatibility +- Verify command permissions +- Review Revit Journal files for errors +- Enable logging for detailed error information -You can refer to the [revit-mcp-commandset](https://github.com/revit-mcp/revit-mcp-commandset) project to develop custom commands. +### Getting Help -## Project File Organization +1. **Enable Logging**: + - Check **Enable Logging** in settings + - Review log files in the plugin directory -``` -revit-mcp-plugin/ -├── Configuration/ # Configuration management related classes -│ ├── CommandConfig.cs # Command configuration -│ ├── ConfigurationManager.cs # Configuration manager -│ ├── DeveloperInfo.cs # Developer information -│ ├── FrameworkConfig.cs # Framework configuration -│ └── ServiceSettings.cs # Service settings -│ -├── Core/ # Program entry and core functionality -│ ├── Application.cs # Application entry point -│ ├── CommandExecutor.cs # Command executor -│ ├── CommandManager.cs # Command manager -│ ├── ExternalEventManager.cs # External event manager -│ ├── MCPServiceConnection.cs # MCP service connection -│ ├── RevitCommandRegistry.cs # Revit command registration -│ ├── Settings.cs # Application settings -│ └── SocketService.cs # Socket service implementation -│ -├── Models/ # Data models -│ └── ... # Various data model classes -│ -├── UI/ # WPF form interfaces -│ └── ... # Interface related classes -│ -└── Utils/ # Utility classes - ├── Logger.cs # Logging utility - └── PathManager.cs # Path management utility -``` +2. **Check Revit Journal**: + - Located in `%APPDATA%\Autodesk\Revit\Journals\` + - Look for errors related to the plugin + +3. **Community Support**: + - [GitHub Issues](https://github.com/neelshah-starline/revit-mcp-plugin/issues) + - [Discussions](https://github.com/neelshah-starline/revit-mcp-plugin/discussions) + +## 🤝 Contributing + +We welcome contributions! Here's how you can help: + +### Development Setup + +1. **Fork the repository** +2. **Create a feature branch**: `git checkout -b feature/amazing-feature` +3. **Make your changes** +4. **Add tests** for new functionality +5. **Ensure all tests pass** +6. **Commit your changes**: `git commit -m 'Add amazing feature'` +7. **Push to branch**: `git push origin feature/amazing-feature` +8. **Open a Pull Request** + +### Contribution Guidelines + +- **📝 Code Style**: Follow existing code patterns and conventions +- **🧪 Testing**: Add tests for new features +- **📚 Documentation**: Update documentation for API changes +- **🔄 Compatibility**: Maintain backward compatibility when possible +- **🐛 Bug Reports**: Use GitHub Issues with detailed information + +### Areas for Contribution -### Configuration Directory -Responsible for managing various configuration information for the plugin: +- 🔧 Bug fixes and performance improvements +- ✨ New features and command implementations +- 📚 Documentation and usage examples +- 🧪 Test coverage expansion +- 🌐 Localization support -- CommandConfig.cs: Defines command-related configuration -- ConfigurationManager.cs: Manages loading, saving, and accessing configurations -- DeveloperInfo.cs: Stores developer-related information -- FrameworkConfig.cs: Framework-level configuration settings -- ServiceSettings.cs: Service-related settings +## 📄 License -### Core Directory -Contains the core functionality and entry point of the plugin: +This project is licensed under the **MIT License** - see the [LICENSE](LICENSE) file for details. -- Application.cs: Application entry point, responsible for initializing the plugin -- CommandExecutor.cs: Core component responsible for executing Revit commands -- CommandManager.cs: Manages and dispatches various commands in the plugin -- ExternalEventManager.cs: Manages Revit external events -- MCPServiceConnection.cs: MCP service connection -- RevitCommandRegistry.cs: Registers and manages available Revit commands -- Settings.cs: Triggers the display of the settings interface -- SocketService.cs: Implements Socket communication with external clients +--- -### Models Directory -Contains data model classes used to pass data between different parts of the system. +
-### UI Directory -Contains user interface related components of the plugin, implemented using the WPF framework. +**Built with ❤️ for the Revit and AI community** -### Utils Directory -Provides various auxiliary tools: +[⭐ Star this project](https://github.com/neelshah-starline/revit-mcp-plugin) | +[🐛 Report issues](https://github.com/neelshah-starline/revit-mcp-plugin/issues) | +[💬 Start discussion](https://github.com/neelshah-starline/revit-mcp-plugin/discussions) -- Logger.cs: Logging tool for debugging and error tracking -- PathManager.cs: Project-related file path management +
From 8d900c222e44944074f6ced5c71d53953b56233c Mon Sep 17 00:00:00 2001 From: neelshah-starline <110639135+neelshah-starline@users.noreply.github.com> Date: Tue, 21 Oct 2025 16:17:20 -0700 Subject: [PATCH 3/7] Remove VendorDescription from README Removed the VendorDescription from the Revit AddIn configuration. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 398bf11..466f49d 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ This plugin works in conjunction with: revit_mcp_plugin.Core.Application 090A4C8C-61DC-426D-87DF-E4BAE0F80EC1 revit-mcp - https://github.com/neelshah-starline/revit-mcp-plugin + ``` From b0269cf1ceafd504f1e79d17c3d2214a859c9c7c Mon Sep 17 00:00:00 2001 From: Neel Shah Date: Fri, 5 Dec 2025 14:30:19 -0800 Subject: [PATCH 4/7] feat: Implement auto-start MCP service and fix button availability - Add ApplicationInitialized event handler to auto-start MCP service on Revit launch - Implement IExternalCommandAvailability class for zero-document button availability - Add diagnostic dialogs for startup verification - Service now starts before any document is opened, port 8080 available immediately - Buttons remain clickable even without open documents - Follows Autodesk official patterns from Jeremy Tammik samples --- revit-mcp-plugin/Core/Application.cs | 40 +++++++++++++++++-- revit-mcp-plugin/Core/MCPServiceConnection.cs | 15 +++++++ 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/revit-mcp-plugin/Core/Application.cs b/revit-mcp-plugin/Core/Application.cs index 4f3597b..b086b2b 100644 --- a/revit-mcp-plugin/Core/Application.cs +++ b/revit-mcp-plugin/Core/Application.cs @@ -1,5 +1,6 @@ using System; using Autodesk.Revit.UI; +using Autodesk.Revit.ApplicationServices; using System.Reflection; using System.Windows.Media.Imaging; @@ -11,6 +12,9 @@ public class Application : IExternalApplication { public Result OnStartup(UIControlledApplication application) { + // Diagnostic: Confirm plugin loads + //TaskDialog.Show("MCP Plugin", "Plugin loaded successfully!"); + RibbonPanel mcpPanel = application.CreateRibbonPanel("Revit MCP Plugin"); PushButtonData pushButtonData = new PushButtonData("ID_EXCMD_TOGGLE_REVIT_MCP", "Revit MCP\r\n Switch", @@ -18,6 +22,7 @@ public Result OnStartup(UIControlledApplication application) pushButtonData.ToolTip = "Open / Close mcp server"; pushButtonData.Image = new BitmapImage(new Uri("/revit-mcp-plugin;component/Core/Ressources/icon-16.png", UriKind.RelativeOrAbsolute)); pushButtonData.LargeImage = new BitmapImage(new Uri("/revit-mcp-plugin;component/Core/Ressources/icon-32.png", UriKind.RelativeOrAbsolute)); + pushButtonData.AvailabilityClassName = "revit_mcp_plugin.Core.MCPCommandAvailability"; mcpPanel.AddItem(pushButtonData); PushButtonData mcp_settings_pushButtonData = new PushButtonData("ID_EXCMD_MCP_SETTINGS", "Settings", @@ -25,21 +30,50 @@ public Result OnStartup(UIControlledApplication application) mcp_settings_pushButtonData.ToolTip = "MCP Settings"; mcp_settings_pushButtonData.Image = new BitmapImage(new Uri("/revit-mcp-plugin;component/Core/Ressources/settings-16.png", UriKind.RelativeOrAbsolute)); mcp_settings_pushButtonData.LargeImage = new BitmapImage(new Uri("/revit-mcp-plugin;component/Core/Ressources/settings-32.png", UriKind.RelativeOrAbsolute)); + mcp_settings_pushButtonData.AvailabilityClassName = "revit_mcp_plugin.Core.MCPCommandAvailability"; mcpPanel.AddItem(mcp_settings_pushButtonData); + // Register for ApplicationInitialized event to auto-start MCP service + // This fires after Revit initialization but before any document opens + application.ControlledApplication.ApplicationInitialized += OnApplicationInitialized; + return Result.Succeeded; } + private void OnApplicationInitialized(object sender, EventArgs e) + { + try + { + // Diagnostic: Confirm ApplicationInitialized event fires + //TaskDialog.Show("MCP Plugin", "ApplicationInitialized event fired - starting MCP service..."); + + // sender is DB-level Autodesk.Revit.ApplicationServices.Application + var app = (Autodesk.Revit.ApplicationServices.Application)sender; + var uiApp = new UIApplication(app); // construct UIApplication in this context + + // Initialize any session services now (pre-document). + SocketService.Instance.Initialize(uiApp); + SocketService.Instance.Start(); + + // Confirm service started + //TaskDialog.Show("MCP Plugin", "MCP service started successfully on port 8080!"); + } + catch (Exception ex) + { + TaskDialog.Show("MCP Plugin Error", $"Failed to start MCP service: {ex.Message}"); + System.Diagnostics.Trace.WriteLine($"Failed to auto-start MCP service: {ex.Message}"); + } + } + public Result OnShutdown(UIControlledApplication application) { + application.ControlledApplication.ApplicationInitialized -= OnApplicationInitialized; try { if (SocketService.Instance.IsRunning) - { SocketService.Instance.Stop(); - } } - catch { } + catch { /* swallow at shutdown to avoid blocking Revit */ } return Result.Succeeded; } diff --git a/revit-mcp-plugin/Core/MCPServiceConnection.cs b/revit-mcp-plugin/Core/MCPServiceConnection.cs index eb37d88..e6f3f04 100644 --- a/revit-mcp-plugin/Core/MCPServiceConnection.cs +++ b/revit-mcp-plugin/Core/MCPServiceConnection.cs @@ -5,9 +5,24 @@ namespace revit_mcp_plugin.Core { + /// + /// Command availability class that allows MCP control even without open documents + /// + public class MCPCommandAvailability : IExternalCommandAvailability + { + public bool IsCommandAvailable(UIApplication applicationData, CategorySet selectedCategories) + { + // Always available - doesn't require document or selection + return true; + } + } + [Transaction(TransactionMode.Manual)] public class MCPServiceConnection : IExternalCommand { + // Make command always available, even without open documents + public string AvailabilityClassName => "revit_mcp_plugin.Core.MCPCommandAvailability"; + public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) { try From e8127eb09b1132d15288170d6dbb949b9ca241d4 Mon Sep 17 00:00:00 2001 From: nshah Date: Sun, 7 Dec 2025 15:25:40 -0800 Subject: [PATCH 5/7] feat: enhance MCP integration and add connection status icon resources - Update Application.cs for improved MCP service integration - Enhance SocketService.cs with better connection handling - Add connection status icon resources (pending UI integration) --- revit-mcp-plugin/Core/Application.cs | 79 ++++---- revit-mcp-plugin/Core/Ressources/Thumbs.db | Bin 0 -> 3072 bytes .../Core/Ressources/server_connected-16.png | Bin 0 -> 4286 bytes .../Core/Ressources/server_connected-32.png | Bin 0 -> 4286 bytes .../Core/Ressources/server_unconnected-16.png | Bin 0 -> 4286 bytes .../Core/Ressources/server_unconnected-32.png | Bin 0 -> 4286 bytes revit-mcp-plugin/Core/SocketService.cs | 185 +++++++++++------- 7 files changed, 154 insertions(+), 110 deletions(-) create mode 100644 revit-mcp-plugin/Core/Ressources/Thumbs.db create mode 100644 revit-mcp-plugin/Core/Ressources/server_connected-16.png create mode 100644 revit-mcp-plugin/Core/Ressources/server_connected-32.png create mode 100644 revit-mcp-plugin/Core/Ressources/server_unconnected-16.png create mode 100644 revit-mcp-plugin/Core/Ressources/server_unconnected-32.png diff --git a/revit-mcp-plugin/Core/Application.cs b/revit-mcp-plugin/Core/Application.cs index b086b2b..9617cde 100644 --- a/revit-mcp-plugin/Core/Application.cs +++ b/revit-mcp-plugin/Core/Application.cs @@ -10,58 +10,67 @@ namespace revit_mcp_plugin.Core { public class Application : IExternalApplication { - public Result OnStartup(UIControlledApplication application) - { - // Diagnostic: Confirm plugin loads - //TaskDialog.Show("MCP Plugin", "Plugin loaded successfully!"); - - RibbonPanel mcpPanel = application.CreateRibbonPanel("Revit MCP Plugin"); - - PushButtonData pushButtonData = new PushButtonData("ID_EXCMD_TOGGLE_REVIT_MCP", "Revit MCP\r\n Switch", - Assembly.GetExecutingAssembly().Location, "revit_mcp_plugin.Core.MCPServiceConnection"); - pushButtonData.ToolTip = "Open / Close mcp server"; - pushButtonData.Image = new BitmapImage(new Uri("/revit-mcp-plugin;component/Core/Ressources/icon-16.png", UriKind.RelativeOrAbsolute)); - pushButtonData.LargeImage = new BitmapImage(new Uri("/revit-mcp-plugin;component/Core/Ressources/icon-32.png", UriKind.RelativeOrAbsolute)); - pushButtonData.AvailabilityClassName = "revit_mcp_plugin.Core.MCPCommandAvailability"; - mcpPanel.AddItem(pushButtonData); - - PushButtonData mcp_settings_pushButtonData = new PushButtonData("ID_EXCMD_MCP_SETTINGS", "Settings", - Assembly.GetExecutingAssembly().Location, "revit_mcp_plugin.Core.Settings"); - mcp_settings_pushButtonData.ToolTip = "MCP Settings"; - mcp_settings_pushButtonData.Image = new BitmapImage(new Uri("/revit-mcp-plugin;component/Core/Ressources/settings-16.png", UriKind.RelativeOrAbsolute)); - mcp_settings_pushButtonData.LargeImage = new BitmapImage(new Uri("/revit-mcp-plugin;component/Core/Ressources/settings-32.png", UriKind.RelativeOrAbsolute)); - mcp_settings_pushButtonData.AvailabilityClassName = "revit_mcp_plugin.Core.MCPCommandAvailability"; - mcpPanel.AddItem(mcp_settings_pushButtonData); - - // Register for ApplicationInitialized event to auto-start MCP service - // This fires after Revit initialization but before any document opens - application.ControlledApplication.ApplicationInitialized += OnApplicationInitialized; + public Result OnStartup(UIControlledApplication application) + { + // Create ribbon panel for MCP controls + RibbonPanel mcpPanel = application.CreateRibbonPanel("Revit MCP Plugin"); - return Result.Succeeded; - } + // Toggle button to start/stop MCP service + PushButtonData pushButtonData = new PushButtonData("ID_EXCMD_TOGGLE_REVIT_MCP", "Revit MCP\r\n Switch", + Assembly.GetExecutingAssembly().Location, "revit_mcp_plugin.Core.MCPServiceConnection"); + pushButtonData.ToolTip = "Start / Stop MCP server"; + pushButtonData.Image = new BitmapImage(new Uri("/revit-mcp-plugin;component/Core/Ressources/icon-16.png", UriKind.RelativeOrAbsolute)); + pushButtonData.LargeImage = new BitmapImage(new Uri("/revit-mcp-plugin;component/Core/Ressources/icon-32.png", UriKind.RelativeOrAbsolute)); + pushButtonData.AvailabilityClassName = "revit_mcp_plugin.Core.MCPCommandAvailability"; + mcpPanel.AddItem(pushButtonData); + + // Settings button + PushButtonData mcp_settings_pushButtonData = new PushButtonData("ID_EXCMD_MCP_SETTINGS", "Settings", + Assembly.GetExecutingAssembly().Location, "revit_mcp_plugin.Core.Settings"); + mcp_settings_pushButtonData.ToolTip = "MCP Settings"; + mcp_settings_pushButtonData.Image = new BitmapImage(new Uri("/revit-mcp-plugin;component/Core/Ressources/settings-16.png", UriKind.RelativeOrAbsolute)); + mcp_settings_pushButtonData.LargeImage = new BitmapImage(new Uri("/revit-mcp-plugin;component/Core/Ressources/settings-32.png", UriKind.RelativeOrAbsolute)); + mcp_settings_pushButtonData.AvailabilityClassName = "revit_mcp_plugin.Core.MCPCommandAvailability"; + mcpPanel.AddItem(mcp_settings_pushButtonData); + + // Register for ApplicationInitialized event to auto-start MCP service + // This fires after Revit initialization but before any document opens + application.ControlledApplication.ApplicationInitialized += OnApplicationInitialized; + + return Result.Succeeded; + } private void OnApplicationInitialized(object sender, EventArgs e) { + System.Diagnostics.Trace.WriteLine("MCP Plugin: ApplicationInitialized event fired"); + try { - // Diagnostic: Confirm ApplicationInitialized event fires - //TaskDialog.Show("MCP Plugin", "ApplicationInitialized event fired - starting MCP service..."); - // sender is DB-level Autodesk.Revit.ApplicationServices.Application var app = (Autodesk.Revit.ApplicationServices.Application)sender; var uiApp = new UIApplication(app); // construct UIApplication in this context + System.Diagnostics.Trace.WriteLine("MCP Plugin: UIApplication created, initializing socket service..."); + // Initialize any session services now (pre-document). SocketService.Instance.Initialize(uiApp); + + System.Diagnostics.Trace.WriteLine("MCP Plugin: Socket service initialized, starting service..."); + SocketService.Instance.Start(); - // Confirm service started - //TaskDialog.Show("MCP Plugin", "MCP service started successfully on port 8080!"); + System.Diagnostics.Trace.WriteLine("MCP Plugin: Socket service started successfully"); + + // Show success message + TaskDialog.Show("MCP Plugin", "MCP service started successfully on port 8080!"); } catch (Exception ex) { - TaskDialog.Show("MCP Plugin Error", $"Failed to start MCP service: {ex.Message}"); - System.Diagnostics.Trace.WriteLine($"Failed to auto-start MCP service: {ex.Message}"); + System.Diagnostics.Trace.WriteLine($"MCP Plugin: CRITICAL ERROR - Failed to start MCP service: {ex.Message}"); + System.Diagnostics.Trace.WriteLine($"MCP Plugin: Stack trace: {ex.StackTrace}"); + + TaskDialog.Show("MCP Plugin Error", + $"Failed to start MCP service: {ex.Message}\n\nCheck Revit journal for details."); } } diff --git a/revit-mcp-plugin/Core/Ressources/Thumbs.db b/revit-mcp-plugin/Core/Ressources/Thumbs.db new file mode 100644 index 0000000000000000000000000000000000000000..b760d2864758252491f46d604d3dceff51c37369 GIT binary patch literal 3072 zcmca`Uhu)fjZzO8(10BSGsD0CoD6J8;*3Bx2!nwD0|OI~0pkDr|NlQkkbwcn90fxt z1pWfu3W`4vW`V}#5OV&gQz;CAAclM(EMZV!aAn8?(nSoFu&hiJEylnK&-3uI0f)|n zN3jwI((f=b1LY5Z3J)MKVlZVeV~A%+WH4kfU`S?2VMqnCEEr6HGKN6D5s;M%Byj;P z1|~-2Ai$*#U3>!6espni_~h76jy<^aP+~tQ&Oku{iW5-&X9Z$Vna>Wy96-zo#9TlO zDj!C{@CyM6VBCV@z5<#bJQs;r=d8Hk{{P1R2MhvUZk}!ojEvBX{r@(DGXo1VGYc~d zI|~atCp#-UCpRY$aP#qi0G}Y3;(<~S4mt@kgpq}fjfaCrjE6@|gpZF;1Vr$OK(q-G zWyk>rK@J811_5S9B?cxzMrJ|A|3?_)fz1(Cpljg{2J)GhSy#U-U>$dGXcJ4ZK_{h;?$4{I*b?NeztJkjI zxOwa0qsLF4K70P+<*SdMK7aZ8?fZ|Pzd-(CWMGDP3+^*Ce+dHp#l!-N7m&XgnaV*P z7i3{oG-MNU3}jC%6jm~7q zU=jYSstV|L(6cxQUJ8O>6%KR-ui+s7d_U!K87f8?_l3G9O!_OXLl=wIMkCa@+BD3B z&oF&%NuSThpVKp8=wZ^e`uuohQTaiB2|m7O|Ow;hOdJ z^5(>q)}OtUgPMjacq6+SuHG7RF^q%7WUKm84l!Jpx9RV3v1bs=k4f`lsR*@=D`8{9 zQXCoNum-NpT#T(VO&z|*F~8IsHFAEh4(PM{?s?LHISI$pzjQ-&!?G9$(jg?>6EP)5 zvL1Bp55w1Ws*T~ZRbJK$r#ueTs0GzRbg&qe+C^~;TXW^u;*&hZ?{lYle1s6!!8lMq zh)xsJBhZ%L2?tN^fYW0~;K|%$i9ve$I$3UNO+&_a>oBJGoZLeWV_J({8Lo%uFb?fM z?uL!*6F>FJ*8S^Y^7gL|n`3ZYF<-@T`5P|fVz^Wnf1S>$7B$unh*`S_`;)Mh6Jc|| z;`QdHmyyHe7zg1Yno)e|KaUgp9p6N$Lf`i}H3{m`?__G`|8nl zeDarzDd&fB@UU!8aNn9A^)L-fVG-&-uXcMv_NQ?ni^KFldu-%0$l)O0yB)O{C+b^2 zf7V0s$e)%w`MBO`xwB8P2_MJdfoA9%{zUlTb@$N69=__VePQ`oU&Qd0&;5t@;PuAk ziQGwluLeBNz?WyU(D&1)ss&;1g+FGeMMuoTzdcW zgGaM2uEmeA=ih_<6UQWeLh}y9K1%f_Klff1wP;kXRxjxj+Pgi412GLE?D>1&)_hZF z)xHue(mXdRdyKHZ8puL>z9Cw}T#dt0K06xw-j48}p}k=yZLjVt(mXfXCV47$TYi_r zmUAk?ww`P{{toJ&u5)EQ(lwcw9);th?T95h93E)m_mI?EG3j0+e_B6sEnkuU^WvHo z-(C2^)w>JNnzkQyO5Ui0!!&&Mi9eg$R)K9X7h>z%@f^mu-9!5@Z`Ite$r`3v+>!PB?f_h!8E2j4JG8_c>>MOp*Rl@35rhL{@p~cK zm*(SdJ-nA%AN9w+8T3qHe%^D`D|=A~S0Ahs=|KO-VXTz1h+|N$UYzJ#&Ew?X_weO8 z`=+Ds58|+0dxdJW1PWJ9rstNzC#iw)OZx3P?S8}|!{>&5OWQO5g{|Zf!p!W=7tVX3 T94qmqcFvFWoA2)e|Cj$M>#uo{ literal 0 HcmV?d00001 diff --git a/revit-mcp-plugin/Core/Ressources/server_connected-32.png b/revit-mcp-plugin/Core/Ressources/server_connected-32.png new file mode 100644 index 0000000000000000000000000000000000000000..719d1dc4129d19b29f83ffa2f0aefe1edeccd717 GIT binary patch literal 4286 zcmcgw&2JS|6d(QrR<3x^1V~tbC_6U9e}FcYje#a6EQ~8mN_4@(L?}h`+9DqnV{5C@ zN^RjGM!+YfEw8o&Z7GNlN=u{)fd-Th``*0W;e6h?GjEENL`!bZoilgN?{|LZ+&e>q zU=jYSstV|L(6cxQUJ8O>6%KR-ui+s7d_U!K87f8?_l3G9O!_OXLl=wIMkCa@+BD3B z&oF&%NuSThpVKp8=wZ^e`uuohQTaiB2|m7O|Ow;hOdJ z^5(>q)}OtUgPMjacq6+SuHG7RF^q%7WUKm84l!Jpx9RV3v1bs=k4f`lsR*@=D`8{9 zQXCoNum-NpT#T(VO&z|*F~8IsHFAEh4(PM{?s?LHISI$pzjQ-&!?G9$(jg?>6EP)5 zvL1Bp55w1Ws*T~ZRbJK$r#ueTs0GzRbg&qe+C^~;TXW^u;*&hZ?{lYle1s6!!8lMq zh)xsJBhZ%L2?tN^fYW0~;K|%$i9ve$I$3UNO+&_a>oBJGoZLeWV_J({8Lo%uFb?fM z?uL!*6F>FJ*8S^Y^7gL|n`3ZYF<-@T`5P|fVz^Wnf1S>$7B$unh*`S_`;)Mh6Jc|| z;`QdHmyyHe7zg1Yno)e|KaUgp9p6N$Lf`i}H3{m`?__G`|8nl zeDarzDd&fB@UU!8aNn9A^)L-fVG-&-uXcMv_NQ?ni^KFldu-%0$l)O0yB)O{C+b^2 zf7V0s$e)%w`MBO`xwB8P2_MJdfoA9%{zUlTb@$N69=__VePQ`oU&Qd0&;5t@;PuAk ziQGwluLeBNz?WyU(D&1)ss&;1g+FGeMMuoTzdcW zgGaM2uEmeA=ih_<6UQWeLh}y9K1%f_Klff1wP;kXRxjxj+Pgi412GLE?D>1&)_hZF z)xHue(mXdRdyKHZ8puL>z9Cw}T#dt0K06xw-j48}p}k=yZLjVt(mXfXCV47$TYi_r zmUAk?ww`P{{toJ&u5)EQ(lwcw9);th?T95h93E)m_mI?EG3j0+e_B6sEnkuU^WvHo z-(C2^)w>JNnzkQyO5Ui0!!&&Mi9eg$R)K9X7h>z%@f^mu-9!5@Z`Ite$r`3v+>!PB?f_h!8E2j4JG8_c>>MOp*Rl@35rhL{@p~cK zm*(SdJ-nA%AN9w+8T3qHe%^D`D|=A~S0Ahs=|KO-VXTz1h+|N$UYzJ#&Ew?X_weO8 z`=+Ds58|+0dxdJW1PWJ9rstNzC#iw)OZx3P?S8}|!{>&5OWQO5g{|Zf!p!W=7tVX3 T94qmqcFvFWoA2)e|Cj$M>#uo{ literal 0 HcmV?d00001 diff --git a/revit-mcp-plugin/Core/Ressources/server_unconnected-16.png b/revit-mcp-plugin/Core/Ressources/server_unconnected-16.png new file mode 100644 index 0000000000000000000000000000000000000000..9da387ba229f960ba7e2a5102288a3d8845dfb33 GIT binary patch literal 4286 zcmcgw-EUMy6ub3CYWFf)h256#l$FB zLcr2!1!If`14g3(f{H?^d%N4BZg*GMZE4GP_w6{p^D%es?N%@*LM}6R=FIth%$d0x zqUbjFcg>mz|3;&?N6{To6m_`A4MqL#!T4%_8jS`u8ck|6o78j<`qpAB97inX{CpT2 zK>>GyR@*2%6eaBCEg)yld&=7FtxgbXvo9&x+tUq&Zkv{u;j+)S-S_RuU zh<@|Ta>;w(O_qy$w^(aaS{&Zla|^VgH%}dR$25L?PWAyjf?VheH*2wX4p-wWH`+#1 zIH$)F9)@rFU)^7(wskQ-%;AJ^U~S09p2&LfM7g=~Ca`qg=CZh^!?(&=)-`B(xk_w?ur(KW# zLG8{Tdv@;nohB|XsczW^-V2vYuo6tu9CE-|$b(}XCvbTAmB0tbVe7-w-j}w{oa^I> zsr&vso&RZ3^R(V?_-44=SKT3gSnr5Ud=MPLOK{sdU7ffGUToZ)r+h*7!*KphEBl|+ zxxG)%)M;J!l;lbc*6+VNv zs@uFre7GmT$1{Gs=Iadk4a-`Lbvxf*+CA&%_70XMm#@DjRVvFgKcCcLD}GjY?7Q-L z^XddK;o_&6K)Jy@V8p8C^|*_`MWUfKH# zJVp**V{EH)(~Y*LFHaYKk(!r0O&S_5Gp_?5USll7Ub{Y~Pd=@s@V)o&8hTl??!R&` z7VFg2gW9vYleXu9JpFocX+=J8E|u!Mcb6uhJ5Oufxji~srSm^Ju5O-j6*KFD-5b0I zxVzkQAqNA=|0 z_pSyt5Yvu<8Nyv?YsEkD?&76I>fPdW1Ka6-_JyMQ7gOiPDf<40h15E(b>IZwj#OUt z>WeSu=-60|?*i1`j)5yQJ1br@zKdhsTU7UGO|jGGX502@s@IddFu@n^;A4nyee?OW z`|jXAYIpjcw3>EnCMK6?6ZYGUA5q=y>*efN-GVpPojIF$V0jJaR$6@f`nDC=U+_hp zZ|WD_s2A}ZXH@DHxWWN^Vmfs?sUO4hHK(b`w0W!QcAg8i+)X&M?6+#S{_q>g-DDa)2hGdl%E!;? zZwK&!FSr}}9`wpCx<7MYN@W(!`(aEpp zx$Z<`vggLb@KKKk56Ru=F#^ptW;iK)^@H_mKa4+gxT+jNeAIi69(ioW?*(%hNpnCR z#&TTXt>v}*(B?yI<7O>l#!u9!tJj@P^&9&4>@9K5!6E#v)tYc;@B{ru7#3sjohw|d z_6?KY!4F)OC-}*G@@*}ebJS}pcw?}*FBtTtvjZ`ua@Ew{DsJ3J4qav|2o|ASAh)~@#d I0{_?e-)QFZ$p8QV literal 0 HcmV?d00001 diff --git a/revit-mcp-plugin/Core/Ressources/server_unconnected-32.png b/revit-mcp-plugin/Core/Ressources/server_unconnected-32.png new file mode 100644 index 0000000000000000000000000000000000000000..9da387ba229f960ba7e2a5102288a3d8845dfb33 GIT binary patch literal 4286 zcmcgw-EUMy6ub3CYWFf)h256#l$FB zLcr2!1!If`14g3(f{H?^d%N4BZg*GMZE4GP_w6{p^D%es?N%@*LM}6R=FIth%$d0x zqUbjFcg>mz|3;&?N6{To6m_`A4MqL#!T4%_8jS`u8ck|6o78j<`qpAB97inX{CpT2 zK>>GyR@*2%6eaBCEg)yld&=7FtxgbXvo9&x+tUq&Zkv{u;j+)S-S_RuU zh<@|Ta>;w(O_qy$w^(aaS{&Zla|^VgH%}dR$25L?PWAyjf?VheH*2wX4p-wWH`+#1 zIH$)F9)@rFU)^7(wskQ-%;AJ^U~S09p2&LfM7g=~Ca`qg=CZh^!?(&=)-`B(xk_w?ur(KW# zLG8{Tdv@;nohB|XsczW^-V2vYuo6tu9CE-|$b(}XCvbTAmB0tbVe7-w-j}w{oa^I> zsr&vso&RZ3^R(V?_-44=SKT3gSnr5Ud=MPLOK{sdU7ffGUToZ)r+h*7!*KphEBl|+ zxxG)%)M;J!l;lbc*6+VNv zs@uFre7GmT$1{Gs=Iadk4a-`Lbvxf*+CA&%_70XMm#@DjRVvFgKcCcLD}GjY?7Q-L z^XddK;o_&6K)Jy@V8p8C^|*_`MWUfKH# zJVp**V{EH)(~Y*LFHaYKk(!r0O&S_5Gp_?5USll7Ub{Y~Pd=@s@V)o&8hTl??!R&` z7VFg2gW9vYleXu9JpFocX+=J8E|u!Mcb6uhJ5Oufxji~srSm^Ju5O-j6*KFD-5b0I zxVzkQAqNA=|0 z_pSyt5Yvu<8Nyv?YsEkD?&76I>fPdW1Ka6-_JyMQ7gOiPDf<40h15E(b>IZwj#OUt z>WeSu=-60|?*i1`j)5yQJ1br@zKdhsTU7UGO|jGGX502@s@IddFu@n^;A4nyee?OW z`|jXAYIpjcw3>EnCMK6?6ZYGUA5q=y>*efN-GVpPojIF$V0jJaR$6@f`nDC=U+_hp zZ|WD_s2A}ZXH@DHxWWN^Vmfs?sUO4hHK(b`w0W!QcAg8i+)X&M?6+#S{_q>g-DDa)2hGdl%E!;? zZwK&!FSr}}9`wpCx<7MYN@W(!`(aEpp zx$Z<`vggLb@KKKk56Ru=F#^ptW;iK)^@H_mKa4+gxT+jNeAIi69(ioW?*(%hNpnCR z#&TTXt>v}*(B?yI<7O>l#!u9!tJj@P^&9&4>@9K5!6E#v)tYc;@B{ru7#3sjohw|d z_6?KY!4F)OC-}*G@@*}ebJS}pcw?}*FBtTtvjZ`ua@Ew{DsJ3J4qav|2o|ASAh)~@#d I0{_?e-)QFZ$p8QV literal 0 HcmV?d00001 diff --git a/revit-mcp-plugin/Core/SocketService.cs b/revit-mcp-plugin/Core/SocketService.cs index 9238bbe..47dc3cb 100644 --- a/revit-mcp-plugin/Core/SocketService.cs +++ b/revit-mcp-plugin/Core/SocketService.cs @@ -50,71 +50,116 @@ public int Port set => _port = value; } - // 初始化 - // Initialization. + // Initialize the socket service public void Initialize(UIApplication uiApp) { - _uiApp = uiApp; + _logger.Info("SocketService: Starting initialization..."); - // 初始化事件管理器 - // Initialize ExternalEventManager - ExternalEventManager.Instance.Initialize(uiApp, _logger); - - // 记录当前 Revit 版本 - // Get the current Revit version. - var versionAdapter = new RevitMCPSDK.API.Utils.RevitVersionAdapter(_uiApp.Application); - string currentVersion = versionAdapter.GetRevitVersion(); - _logger.Info("当前 Revit 版本: {0}\nCurrent Revit version: {0}", currentVersion); - - - - // 创建命令执行器 - // Create CommandExecutor - _commandExecutor = new CommandExecutor(_commandRegistry, _logger); - - // 加载配置并注册命令 - // Load configuration and register commands. - ConfigurationManager configManager = new ConfigurationManager(_logger); - configManager.LoadConfiguration(); - - - //// 从配置中读取服务端口 - //// Read the service port from the configuration. - //if (configManager.Config.Settings.Port > 0) - //{ - // _port = configManager.Config.Settings.Port; - //} - _port = 8080; // 固定端口号 - Hard-wired port number. - - // 加载命令 - // Load command. - CommandManager commandManager = new CommandManager( - _commandRegistry, _logger, configManager, _uiApp); - commandManager.LoadCommands(); - - _logger.Info($"Socket service initialized on port {_port}"); + try + { + _uiApp = uiApp; + _logger.Info("SocketService: UIApplication assigned"); + + // Initialize ExternalEventManager + _logger.Info("SocketService: Initializing ExternalEventManager..."); + ExternalEventManager.Instance.Initialize(uiApp, _logger); + _logger.Info("SocketService: ExternalEventManager initialized"); + + // Get the current Revit version + _logger.Info("SocketService: Getting Revit version..."); + var versionAdapter = new RevitMCPSDK.API.Utils.RevitVersionAdapter(_uiApp.Application); + string currentVersion = versionAdapter.GetRevitVersion(); + _logger.Info($"Current Revit version: {currentVersion}"); + + // Create CommandExecutor + _logger.Info("SocketService: Creating CommandExecutor..."); + _commandExecutor = new CommandExecutor(_commandRegistry, _logger); + _logger.Info("SocketService: CommandExecutor created"); + + // Load configuration and register commands + _logger.Info("SocketService: Loading configuration..."); + ConfigurationManager configManager = new ConfigurationManager(_logger); + configManager.LoadConfiguration(); + _logger.Info("SocketService: Configuration loaded"); + + //// Read the service port from the configuration + //if (configManager.Config.Settings.Port > 0) + //{ + // _port = configManager.Config.Settings.Port; + //} + _port = 8080; // Hard-coded port number + _logger.Info($"SocketService: Using port {_port}"); + + // Load commands + _logger.Info("SocketService: Loading commands..."); + CommandManager commandManager = new CommandManager( + _commandRegistry, _logger, configManager, _uiApp); + commandManager.LoadCommands(); + _logger.Info("SocketService: Commands loaded"); + + _logger.Info($"Socket service initialized successfully on port {_port}"); + } + catch (Exception ex) + { + _logger.Error($"SocketService initialization failed: {ex.Message}"); + _logger.Error($"Stack trace: {ex.StackTrace}"); + throw; // Re-throw to let caller handle it + } } public void Start() { - if (_isRunning) return; + if (_isRunning) + { + _logger.Info("SocketService: Service is already running"); + return; + } - try + // Try to start on the configured port, with fallback ports if needed + int[] portsToTry = { _port, 8081, 8082, 8083, 8084, 8085 }; + + foreach (int portToTry in portsToTry) { - _isRunning = true; - _listener = new TcpListener(IPAddress.Any, _port); - _listener.Start(); + try + { + _logger.Info($"SocketService: Attempting to start TCP listener on port {portToTry}..."); + + _isRunning = true; + _listener = new TcpListener(IPAddress.Any, portToTry); + _listener.Start(); - _listenerThread = new Thread(ListenForClients) + _port = portToTry; // Update the actual port being used + _logger.Info($"SocketService: TCP listener started successfully on port {_port}"); + + _listenerThread = new Thread(ListenForClients) + { + IsBackground = true, + Name = "RevitMCP-SocketListener" + }; + _listenerThread.Start(); + + _logger.Info("SocketService: Listener thread started"); + return; // Success, exit the loop + } + catch (SocketException ex) when (ex.SocketErrorCode == SocketError.AddressAlreadyInUse) { - IsBackground = true - }; - _listenerThread.Start(); - } - catch (Exception) - { - _isRunning = false; + _logger.Warning($"SocketService: Port {portToTry} is already in use, trying next port..."); + _isRunning = false; + continue; // Try next port + } + catch (Exception ex) + { + _logger.Error($"SocketService: Failed to start socket service on port {portToTry}: {ex.Message}"); + _logger.Error($"Stack trace: {ex.StackTrace}"); + _isRunning = false; + throw; // Re-throw for critical errors (not port conflicts) + } } + + // If we get here, all ports failed + _logger.Error("SocketService: Failed to start on any available port (8080-8085)"); + _isRunning = false; + throw new Exception("Could not start socket service - all ports (8080-8085) are in use"); } public void Stop() @@ -175,8 +220,7 @@ private void HandleClientCommunication(object clientObj) while (_isRunning && tcpClient.Connected) { - // 读取客户端消息 - // Read client messages. + // Read client messages int bytesRead = 0; try @@ -185,25 +229,22 @@ private void HandleClientCommunication(object clientObj) } catch (IOException) { - // 客户端断开连接 - // Client disconnected. + // Client disconnected break; } if (bytesRead == 0) { - // 客户端断开连接 - // Client disconnected. + // Client disconnected break; } string message = Encoding.UTF8.GetString(buffer, 0, bytesRead); - System.Diagnostics.Trace.WriteLine($"收到消息: {message}\nReceived message: {message}"); + System.Diagnostics.Trace.WriteLine($"Received message: {message}"); string response = ProcessJsonRPCRequest(message); - // 发送响应 - // Send response. + // Send response byte[] responseData = Encoding.UTF8.GetBytes(response); stream.Write(responseData, 0, responseData.Length); } @@ -224,12 +265,10 @@ private string ProcessJsonRPCRequest(string requestJson) try { - // 解析JSON-RPC请求 - // Parse JSON-RPC requests. + // Parse JSON-RPC requests request = JsonConvert.DeserializeObject(requestJson); - // 验证请求格式是否有效 - // Verify that the request format is valid. + // Verify that the request format is valid if (request == null || !request.IsValid()) { return CreateErrorResponse( @@ -239,18 +278,16 @@ private string ProcessJsonRPCRequest(string requestJson) ); } - // 查找命令 - // Search for the command in the registry. + // Search for the command in the registry if (!_commandRegistry.TryGetCommand(request.Method, out var command)) { return CreateErrorResponse(request.Id, JsonRPCErrorCodes.MethodNotFound, $"Method '{request.Method}' not found"); } - // 执行命令 - // Execute command. + // Execute command try - { + { object result = command.Execute(request.GetParamsObject(), request.Id); return CreateSuccessResponse(request.Id, result); @@ -262,8 +299,7 @@ private string ProcessJsonRPCRequest(string requestJson) } catch (JsonException) { - // JSON解析错误 - // JSON parsing error. + // JSON parsing error return CreateErrorResponse( null, JsonRPCErrorCodes.ParseError, @@ -272,8 +308,7 @@ private string ProcessJsonRPCRequest(string requestJson) } catch (Exception ex) { - // 处理请求时的其他错误 - // Catch other errors produced when processing requests. + // Catch other errors produced when processing requests return CreateErrorResponse( null, JsonRPCErrorCodes.InternalError, From b6563471fa281a216615c1a3fa1172b4a8388575 Mon Sep 17 00:00:00 2001 From: Neel Shah Date: Mon, 8 Dec 2025 10:31:57 -0800 Subject: [PATCH 6/7] feat: enhance plugin architecture for application info command - Update Application.cs with improved auto-start and connection handling - Enhance MCPServiceConnection.cs for better service reliability - Improve SocketService.cs with enhanced communication protocols - Add Commands directory structure for command registry management - Update commandRegistry.json to include all available MCP commands These infrastructure improvements support the new get_revit_application_info command and improve overall plugin stability and command discovery. --- .../Commands/commandRegistry.json | 202 ++++++++++++++++++ revit-mcp-plugin/Core/Application.cs | 82 +++++-- revit-mcp-plugin/Core/MCPServiceConnection.cs | 19 +- revit-mcp-plugin/Core/Ressources/Thumbs.db | Bin 3072 -> 4096 bytes revit-mcp-plugin/Core/SocketService.cs | 9 + 5 files changed, 276 insertions(+), 36 deletions(-) create mode 100644 revit-mcp-plugin/Commands/commandRegistry.json diff --git a/revit-mcp-plugin/Commands/commandRegistry.json b/revit-mcp-plugin/Commands/commandRegistry.json new file mode 100644 index 0000000..6c33cc2 --- /dev/null +++ b/revit-mcp-plugin/Commands/commandRegistry.json @@ -0,0 +1,202 @@ +{ + "commands": [ + { + "commandName": "ai_element_filter", + "assemblyPath": "RevitMCPCommandSet.dll", + "enabled": true, + "supportedRevitVersions": [], + "developer": { + "name": "Revit MCP Team", + "email": "support@revitmcp.com" + }, + "description": "Filters Revit elements using AI-powered criteria" + }, + { + "commandName": "color_splash", + "assemblyPath": "RevitMCPCommandSet.dll", + "enabled": true, + "supportedRevitVersions": [], + "developer": { + "name": "Revit MCP Team", + "email": "support@revitmcp.com" + }, + "description": "Applies color coding to elements based on specified parameters" + }, + { + "commandName": "create_line_based_element", + "assemblyPath": "RevitMCPCommandSet.dll", + "enabled": true, + "supportedRevitVersions": [], + "developer": { + "name": "Revit MCP Team", + "email": "support@revitmcp.com" + }, + "description": "Creates line-based elements like walls and beams" + }, + { + "commandName": "create_point_based_element", + "assemblyPath": "RevitMCPCommandSet.dll", + "enabled": true, + "supportedRevitVersions": [], + "developer": { + "name": "Revit MCP Team", + "email": "support@revitmcp.com" + }, + "description": "Creates point-based elements like doors and furniture" + }, + { + "commandName": "create_surface_based_element", + "assemblyPath": "RevitMCPCommandSet.dll", + "enabled": true, + "supportedRevitVersions": [], + "developer": { + "name": "Revit MCP Team", + "email": "support@revitmcp.com" + }, + "description": "Creates surface-based elements like floors and ceilings" + }, + { + "commandName": "operate_element", + "assemblyPath": "RevitMCPCommandSet.dll", + "enabled": true, + "supportedRevitVersions": [], + "developer": { + "name": "Revit MCP Team", + "email": "support@revitmcp.com" + }, + "description": "Performs operations on selected elements (select, hide, isolate, etc.)" + }, + { + "commandName": "tag_all_walls", + "assemblyPath": "RevitMCPCommandSet.dll", + "enabled": true, + "supportedRevitVersions": [], + "developer": { + "name": "Revit MCP Team", + "email": "support@revitmcp.com" + }, + "description": "Creates tags for all walls in the current view" + }, + { + "commandName": "get_available_family_types", + "assemblyPath": "RevitMCPCommandSet.dll", + "enabled": true, + "supportedRevitVersions": [], + "developer": { + "name": "Revit MCP Team", + "email": "support@revitmcp.com" + }, + "description": "Retrieves all available family types in the project" + }, + { + "commandName": "get_current_view_elements", + "assemblyPath": "RevitMCPCommandSet.dll", + "enabled": true, + "supportedRevitVersions": [], + "developer": { + "name": "Revit MCP Team", + "email": "support@revitmcp.com" + }, + "description": "Gets all elements visible in the current view" + }, + { + "commandName": "get_current_view_info", + "assemblyPath": "RevitMCPCommandSet.dll", + "enabled": true, + "supportedRevitVersions": [], + "developer": { + "name": "Revit MCP Team", + "email": "support@revitmcp.com" + }, + "description": "Retrieves information about the current active view" + }, + { + "commandName": "get_selected_elements", + "assemblyPath": "RevitMCPCommandSet.dll", + "enabled": true, + "supportedRevitVersions": [], + "developer": { + "name": "Revit MCP Team", + "email": "support@revitmcp.com" + }, + "description": "Gets information about currently selected elements" + }, + { + "commandName": "create_dimensions", + "assemblyPath": "RevitMCPCommandSet.dll", + "enabled": true, + "supportedRevitVersions": [], + "developer": { + "name": "Revit MCP Team", + "email": "support@revitmcp.com" + }, + "description": "Creates dimension annotations in the model" + }, + { + "commandName": "delete_element", + "assemblyPath": "RevitMCPCommandSet.dll", + "enabled": true, + "supportedRevitVersions": [], + "developer": { + "name": "Revit MCP Team", + "email": "support@revitmcp.com" + }, + "description": "Deletes elements using ElementId" + }, + { + "commandName": "send_code_to_revit", + "assemblyPath": "RevitMCPCommandSet.dll", + "enabled": true, + "supportedRevitVersions": [], + "developer": { + "name": "Revit MCP Team", + "email": "support@revitmcp.com" + }, + "description": "Executes dynamic C# code within the Revit environment" + }, + { + "commandName": "get_current_document_info", + "assemblyPath": "RevitMCPCommandSet.dll", + "enabled": true, + "supportedRevitVersions": [], + "developer": { + "name": "Revit MCP Team", + "email": "support@revitmcp.com" + }, + "description": "Retrieves comprehensive metadata about the currently active Revit document" + }, + { + "commandName": "get_element_parameters", + "assemblyPath": "RevitMCPCommandSet.dll", + "enabled": true, + "supportedRevitVersions": [], + "developer": { + "name": "Revit MCP Team", + "email": "support@revitmcp.com" + }, + "description": "Retrieves all parameters and their current values for a specific Revit element" + }, + { + "commandName": "set_element_parameters", + "assemblyPath": "RevitMCPCommandSet.dll", + "enabled": true, + "supportedRevitVersions": [], + "developer": { + "name": "Revit MCP Team", + "email": "support@revitmcp.com" + }, + "description": "Modifies parameter values for a specific Revit element" + }, + { + "commandName": "get_revit_application_info", + "assemblyPath": "RevitMCPCommandSet.dll", + "enabled": true, + "supportedRevitVersions": [], + "developer": { + "name": "Revit MCP Team", + "email": "support@revitmcp.com" + }, + "description": "Returns session-level Revit application metadata without requiring an open document" + } + ] +} diff --git a/revit-mcp-plugin/Core/Application.cs b/revit-mcp-plugin/Core/Application.cs index 9617cde..92ef920 100644 --- a/revit-mcp-plugin/Core/Application.cs +++ b/revit-mcp-plugin/Core/Application.cs @@ -1,5 +1,6 @@ using System; using Autodesk.Revit.UI; +using Autodesk.Revit.UI.Events; using Autodesk.Revit.ApplicationServices; using System.Reflection; using System.Windows.Media.Imaging; @@ -10,7 +11,11 @@ namespace revit_mcp_plugin.Core { public class Application : IExternalApplication { - public Result OnStartup(UIControlledApplication application) + // Keep references for adaptive UI + private static PushButton _mcpToggleButton; + private static UIApplication _uiApp; + + public Result OnStartup(UIControlledApplication application) { // Create ribbon panel for MCP controls RibbonPanel mcpPanel = application.CreateRibbonPanel("Revit MCP Plugin"); @@ -19,10 +24,11 @@ public Result OnStartup(UIControlledApplication application) PushButtonData pushButtonData = new PushButtonData("ID_EXCMD_TOGGLE_REVIT_MCP", "Revit MCP\r\n Switch", Assembly.GetExecutingAssembly().Location, "revit_mcp_plugin.Core.MCPServiceConnection"); pushButtonData.ToolTip = "Start / Stop MCP server"; - pushButtonData.Image = new BitmapImage(new Uri("/revit-mcp-plugin;component/Core/Ressources/icon-16.png", UriKind.RelativeOrAbsolute)); - pushButtonData.LargeImage = new BitmapImage(new Uri("/revit-mcp-plugin;component/Core/Ressources/icon-32.png", UriKind.RelativeOrAbsolute)); + // Initial images: red (stopped) before ApplicationInitialized auto-start kicks in + pushButtonData.Image = new BitmapImage(new Uri(@"U:\SW Python 3 Automations\RevitMCP\revit-mcp-plugin\revit-mcp-plugin\Core\Ressources\server_unconnected-16.png", UriKind.Absolute)); + pushButtonData.LargeImage = new BitmapImage(new Uri(@"U:\SW Python 3 Automations\RevitMCP\revit-mcp-plugin\revit-mcp-plugin\Core\Ressources\server_unconnected-32.png", UriKind.Absolute)); pushButtonData.AvailabilityClassName = "revit_mcp_plugin.Core.MCPCommandAvailability"; - mcpPanel.AddItem(pushButtonData); + _mcpToggleButton = mcpPanel.AddItem(pushButtonData) as PushButton; // Settings button PushButtonData mcp_settings_pushButtonData = new PushButtonData("ID_EXCMD_MCP_SETTINGS", "Settings", @@ -42,43 +48,75 @@ public Result OnStartup(UIControlledApplication application) private void OnApplicationInitialized(object sender, EventArgs e) { - System.Diagnostics.Trace.WriteLine("MCP Plugin: ApplicationInitialized event fired"); - try { - // sender is DB-level Autodesk.Revit.ApplicationServices.Application var app = (Autodesk.Revit.ApplicationServices.Application)sender; - var uiApp = new UIApplication(app); // construct UIApplication in this context - - System.Diagnostics.Trace.WriteLine("MCP Plugin: UIApplication created, initializing socket service..."); - - // Initialize any session services now (pre-document). - SocketService.Instance.Initialize(uiApp); - - System.Diagnostics.Trace.WriteLine("MCP Plugin: Socket service initialized, starting service..."); + _uiApp = new UIApplication(app); - SocketService.Instance.Start(); + // Initialize and AUTO-START your socket service + SocketService.Instance.Initialize(_uiApp); + SocketService.Instance.RunningStateChanged += OnRunningStateChanged; + SocketService.Instance.Start(); // auto-start - System.Diagnostics.Trace.WriteLine("MCP Plugin: Socket service started successfully"); + // Immediately reflect "Running" (green) in the button + UpdateToggleButtonVisual(SocketService.Instance.IsRunning); - // Show success message - TaskDialog.Show("MCP Plugin", "MCP service started successfully on port 8080!"); + // Optional: a non-intrusive message (comment out if you prefer silent) + // TaskDialog.Show("MCP Plugin", "MCP service started successfully on port 8080!"); } catch (Exception ex) { - System.Diagnostics.Trace.WriteLine($"MCP Plugin: CRITICAL ERROR - Failed to start MCP service: {ex.Message}"); - System.Diagnostics.Trace.WriteLine($"MCP Plugin: Stack trace: {ex.StackTrace}"); - TaskDialog.Show("MCP Plugin Error", $"Failed to start MCP service: {ex.Message}\n\nCheck Revit journal for details."); } } + // Event handler fired by the service whenever run/stop state changes + private void OnRunningStateChanged(object sender, bool isRunning) + { + // Ensure UI changes occur on Revit's UI thread via Idling + if (_uiApp != null) + { + EventHandler idleOnce = null; + idleOnce = (s, e) => + { + _uiApp.Idling -= idleOnce; + UpdateToggleButtonVisual(isRunning); + }; + _uiApp.Idling += idleOnce; + } + } + + // Swap images/text based on service state + private void UpdateToggleButtonVisual(bool isRunning) + { + if (_mcpToggleButton == null) return; + + string small = isRunning + ? @"U:\SW Python 3 Automations\RevitMCP\revit-mcp-plugin\revit-mcp-plugin\Core\Ressources\server_connected-16.png" + : @"U:\SW Python 3 Automations\RevitMCP\revit-mcp-plugin\revit-mcp-plugin\Core\Ressources\server_unconnected-16.png"; + + string large = isRunning + ? @"U:\SW Python 3 Automations\RevitMCP\revit-mcp-plugin\revit-mcp-plugin\Core\Ressources\server_connected-32.png" + : @"U:\SW Python 3 Automations\RevitMCP\revit-mcp-plugin\revit-mcp-plugin\Core\Ressources\server_unconnected-32.png"; + + _mcpToggleButton.Image = new BitmapImage(new Uri(small, UriKind.Absolute)); + _mcpToggleButton.LargeImage = new BitmapImage(new Uri(large, UriKind.Absolute)); + + // Optional: adjust text & tooltip + _mcpToggleButton.ItemText = isRunning ? "MCP (Running)" : "MCP (Stopped)"; + _mcpToggleButton.ToolTip = isRunning + ? "MCP server is running. Click to stop." + : "MCP server is stopped. Click to start."; + } + public Result OnShutdown(UIControlledApplication application) { application.ControlledApplication.ApplicationInitialized -= OnApplicationInitialized; + try { + SocketService.Instance.RunningStateChanged -= OnRunningStateChanged; if (SocketService.Instance.IsRunning) SocketService.Instance.Stop(); } diff --git a/revit-mcp-plugin/Core/MCPServiceConnection.cs b/revit-mcp-plugin/Core/MCPServiceConnection.cs index e6f3f04..e2ad193 100644 --- a/revit-mcp-plugin/Core/MCPServiceConnection.cs +++ b/revit-mcp-plugin/Core/MCPServiceConnection.cs @@ -27,22 +27,13 @@ public Result Execute(ExternalCommandData commandData, ref string message, Eleme { try { - // 获取socket服务 - // Obtain socket service. - SocketService service = SocketService.Instance; - - if (service.IsRunning) - { - service.Stop(); - TaskDialog.Show("revitMCP", "Close Server"); - } + // Simple toggle: start if stopped, stop if running + if (SocketService.Instance.IsRunning) + SocketService.Instance.Stop(); else - { - service.Initialize(commandData.Application); - service.Start(); - TaskDialog.Show("revitMCP", "Open Server"); - } + SocketService.Instance.Start(); + // UI updates automatically via RunningStateChanged event return Result.Succeeded; } catch (Exception ex) diff --git a/revit-mcp-plugin/Core/Ressources/Thumbs.db b/revit-mcp-plugin/Core/Ressources/Thumbs.db index b760d2864758252491f46d604d3dceff51c37369..a1191fd36bc298ca1bb263c52e1d3e43b84dcd2b 100644 GIT binary patch delta 880 zcmZpWXi(T7z{JYRz`($^nV0D`<0O_GzJf28r@c?V!^jL2J-|M>ip7hE3CR2p1(O+B zHS{NdM1a7E!IZ&_A)dj6!JNU6ArVNLG9&?EGJ^pSCoxz8WsMk&Co8f@3TQDf!3_bK zHTfco3L7`jkcTXj16jQ$A7Rb#N8KpY~7T6bg~4;5@rPk zh0XgpPB7O0KGc;nNk!w5$H72Hog)tzELA2SVBTrN^(i*}NL2jMUY8&F4o3TLioaD3 zZQNg)Z^zMFP%*;i1 z9+<~eS)FeGK_mKmQJMNn{hQNXeS3V+&3eWmN6|Cg%&gpdIe3mdmhPCa>Aj`8bn&O_ zlf4D4Hb0rZJGbQS(u>G^WH@7L9^uHcqrNp-T>nXr3N80!_`#!2?=mW5LorZN24{$cZ@V@p4r``VM0 zXL7SIZM(Xp#eou=w}l7PB~pJz6fCWq^YK5!;kDV1#P;Uqu87sC6K-|sXK`6mKgo;# zZru^Vi;X|S6*P+XZAtr~`oQul%av%K9#4rFrD+j;7h81&l#CBZo_e@zLy~W3mhj5p zd3NvhI`{33_Kf$xhilhqtL$sA%5 zR}H0D{HG+mb)2wZo>NodW%adDp&U#r4#+TQUVV9JR^RTuvtRwW|8?uMlk(D?M^9~= f<;J;PLR;gYt25+21$97w;z$jrdNaDZuY6^j?oe _isRunning; + public event EventHandler RunningStateChanged; + public int Port { get => _port; @@ -139,6 +141,10 @@ public void Start() _listenerThread.Start(); _logger.Info("SocketService: Listener thread started"); + + // Raise event for state change + RunningStateChanged?.Invoke(this, _isRunning); + return; // Success, exit the loop } catch (SocketException ex) when (ex.SocketErrorCode == SocketError.AddressAlreadyInUse) @@ -177,6 +183,9 @@ public void Stop() { _listenerThread.Join(1000); } + + // Raise event for state change + RunningStateChanged?.Invoke(this, _isRunning); } catch (Exception) { From 80cbcf8afde48942a963ff851b2063e07cddc53b Mon Sep 17 00:00:00 2001 From: Neel Shah Date: Mon, 8 Dec 2025 12:04:58 -0800 Subject: [PATCH 7/7] feat(assets): update server connection icons Updated PNG icons for server connected/unconnected states (16px and 32px) in the Resources directory. These changes improve visual indicators for server connectivity in the Revit MCP plugin. --- .../Core/Ressources/server_connected-16.png | Bin 4286 -> 604 bytes .../Core/Ressources/server_connected-32.png | Bin 4286 -> 1246 bytes .../Core/Ressources/server_unconnected-32.png | Bin 4286 -> 1336 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/revit-mcp-plugin/Core/Ressources/server_connected-16.png b/revit-mcp-plugin/Core/Ressources/server_connected-16.png index 719d1dc4129d19b29f83ffa2f0aefe1edeccd717..d1bca3454b113d79dd0fab8dbe466d83f75c2d81 100644 GIT binary patch literal 604 zcmV-i0;BzjP)bNjQrb$T%O*B`(H1GBN!nEjg+jFzq4brVv{FNxD(FfSjo?B> zP(ctuMeu>5i=Yl_RFD{m8pQ-P8Usd+iH~^ijEqV$C_0>T=D+{_?>7$wm%J_nNV=Dz zRF-`Xsf%-s=K?%U75CifX#}INbE+g$Pxswh9 zy#Jn+F?BmoIO3Xy?cfevVHjX&1{BHnNuKNYt8oXMz#|OMV;g>neXa<^zZU>021sZG z=@^0#4KZV$S6l#(FaSclMd3}-BTgFx_jZAN3PaPNnoo?bE~^^A+g$N!PV!^D*Y-at zCZ1p5Ng(W^6%bG$!T-bTw*JDlP)JZmBD?;dZ+UYaXbM3Hd!%KI6ar(LQH>u9DQm0Q zh;5-x0Y23jR{It#(ZNxd$&ud{b#pFMlB0OjZ7=FcXf7@F3#U#90gzSs+upx4d2N5n z-LTwh(c_GNu(l)DsQIb}pKlGXBbESImG~V8bKXu@6Pp<^DPjtM&M)OEXRTQ)k`uKL q0RRC1|LxkS5C8xG21!IgR09B}2%;eXDY7~M0000q zU=jYSstV|L(6cxQUJ8O>6%KR-ui+s7d_U!K87f8?_l3G9O!_OXLl=wIMkCa@+BD3B z&oF&%NuSThpVKp8=wZ^e`uuohQTaiB2|m7O|Ow;hOdJ z^5(>q)}OtUgPMjacq6+SuHG7RF^q%7WUKm84l!Jpx9RV3v1bs=k4f`lsR*@=D`8{9 zQXCoNum-NpT#T(VO&z|*F~8IsHFAEh4(PM{?s?LHISI$pzjQ-&!?G9$(jg?>6EP)5 zvL1Bp55w1Ws*T~ZRbJK$r#ueTs0GzRbg&qe+C^~;TXW^u;*&hZ?{lYle1s6!!8lMq zh)xsJBhZ%L2?tN^fYW0~;K|%$i9ve$I$3UNO+&_a>oBJGoZLeWV_J({8Lo%uFb?fM z?uL!*6F>FJ*8S^Y^7gL|n`3ZYF<-@T`5P|fVz^Wnf1S>$7B$unh*`S_`;)Mh6Jc|| z;`QdHmyyHe7zg1Yno)e|KaUgp9p6N$Lf`i}H3{m`?__G`|8nl zeDarzDd&fB@UU!8aNn9A^)L-fVG-&-uXcMv_NQ?ni^KFldu-%0$l)O0yB)O{C+b^2 zf7V0s$e)%w`MBO`xwB8P2_MJdfoA9%{zUlTb@$N69=__VePQ`oU&Qd0&;5t@;PuAk ziQGwluLeBNz?WyU(D&1)ss&;1g+FGeMMuoTzdcW zgGaM2uEmeA=ih_<6UQWeLh}y9K1%f_Klff1wP;kXRxjxj+Pgi412GLE?D>1&)_hZF z)xHue(mXdRdyKHZ8puL>z9Cw}T#dt0K06xw-j48}p}k=yZLjVt(mXfXCV47$TYi_r zmUAk?ww`P{{toJ&u5)EQ(lwcw9);th?T95h93E)m_mI?EG3j0+e_B6sEnkuU^WvHo z-(C2^)w>JNnzkQyO5Ui0!!&&Mi9eg$R)K9X7h>z%@f^mu-9!5@Z`Ite$r`3v+>!PB?f_h!8E2j4JG8_c>>MOp*Rl@35rhL{@p~cK zm*(SdJ-nA%AN9w+8T3qHe%^D`D|=A~S0Ahs=|KO-VXTz1h+|N$UYzJ#&Ew?X_weO8 z`=+Ds58|+0dxdJW1PWJ9rstNzC#iw)OZx3P?S8}|!{>&5OWQO5g{|Zf!p!W=7tVX3 T94qmqcFvFWoA2)e|Cj$M>#uo{ diff --git a/revit-mcp-plugin/Core/Ressources/server_connected-32.png b/revit-mcp-plugin/Core/Ressources/server_connected-32.png index 719d1dc4129d19b29f83ffa2f0aefe1edeccd717..748bd101dfbbb77256f1cff33b48954f52acff04 100644 GIT binary patch literal 1246 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 T94qmqcFvFWoA2)e|Cj$M>#uo{ diff --git a/revit-mcp-plugin/Core/Ressources/server_unconnected-32.png b/revit-mcp-plugin/Core/Ressources/server_unconnected-32.png index 9da387ba229f960ba7e2a5102288a3d8845dfb33..d3dfa999bf707247b9f2a3a168d8e778cb5b404f 100644 GIT binary patch literal 1336 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 I0{_?e-)QFZ$p8QV