├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── README_zh.md ├── SampleCommandSet ├── Commands │ ├── Access │ │ ├── GetAvailableFamilyTypesCommand.cs │ │ ├── GetAvailableFamilyTypesEventHandler.cs │ │ ├── GetCurrentViewElementsCommand.cs │ │ ├── GetCurrentViewElementsEventHandler.cs │ │ ├── GetCurrentViewInfoCommand.cs │ │ ├── GetCurrentViewInfoEventHandler.cs │ │ ├── GetSelectedElementsCommand.cs │ │ └── GetSelectedElementsEventHandler.cs │ ├── Create │ │ ├── CreateWallCommand.cs │ │ └── CreateWallEventHandler.cs │ ├── Delete │ │ ├── DeleteElementCommand.cs │ │ └── DeleteElementEventHandler.cs │ └── Test │ │ ├── SayHelloCommand.cs │ │ └── SayHelloEventHandler.cs ├── Extensions │ └── RevitApiCompatibilityExtensions.cs ├── Models │ ├── FamilyTypeInfo.cs │ ├── Point.cs │ ├── ViewInfo.cs │ └── WallInfo.cs ├── Properties │ └── AssemblyInfo.cs ├── SampleCommandSet.csproj ├── command.json └── packages.config ├── revit-mcp-plugin.sln └── 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 ├── Ressources │ ├── icon-16.png │ ├── icon-32.png │ ├── settings-16.png │ └── settings-32.png ├── RevitCommandRegistry.cs ├── Settings.cs └── SocketService.cs ├── Properties └── AssemblyInfo.cs ├── UI ├── CommandSetSettingsPage.xaml ├── CommandSetSettingsPage.xaml.cs ├── SettingsWindow.xaml └── SettingsWindow.xaml.cs ├── Utils ├── Logger.cs └── PathManager.cs ├── revit-mcp-plugin.csproj └── revit-mcp.addin /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Oo]ut/ 33 | [Ll]og/ 34 | [Ll]ogs/ 35 | 36 | # Visual Studio 2015/2017 cache/options directory 37 | .vs/ 38 | # Uncomment if you have tasks that create the project's static files in wwwroot 39 | #wwwroot/ 40 | 41 | # Visual Studio 2017 auto generated files 42 | Generated\ Files/ 43 | 44 | # MSTest test Results 45 | [Tt]est[Rr]esult*/ 46 | [Bb]uild[Ll]og.* 47 | 48 | # NUnit 49 | *.VisualState.xml 50 | TestResult.xml 51 | nunit-*.xml 52 | 53 | # Build Results of an ATL Project 54 | [Dd]ebugPS/ 55 | [Rr]eleasePS/ 56 | dlldata.c 57 | 58 | # Benchmark Results 59 | BenchmarkDotNet.Artifacts/ 60 | 61 | # .NET Core 62 | project.lock.json 63 | project.fragment.lock.json 64 | artifacts/ 65 | 66 | # ASP.NET Scaffolding 67 | ScaffoldingReadMe.txt 68 | 69 | # StyleCop 70 | StyleCopReport.xml 71 | 72 | # Files built by Visual Studio 73 | *_i.c 74 | *_p.c 75 | *_h.h 76 | *.ilk 77 | *.meta 78 | *.obj 79 | *.iobj 80 | *.pch 81 | *.pdb 82 | *.ipdb 83 | *.pgc 84 | *.pgd 85 | *.rsp 86 | *.sbr 87 | *.tlb 88 | *.tli 89 | *.tlh 90 | *.tmp 91 | *.tmp_proj 92 | *_wpftmp.csproj 93 | *.log 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio LightSwitch build output 298 | **/*.HTMLClient/GeneratedArtifacts 299 | **/*.DesktopClient/GeneratedArtifacts 300 | **/*.DesktopClient/ModelManifest.xml 301 | **/*.Server/GeneratedArtifacts 302 | **/*.Server/ModelManifest.xml 303 | _Pvt_Extensions 304 | 305 | # Paket dependency manager 306 | .paket/paket.exe 307 | paket-files/ 308 | 309 | # FAKE - F# Make 310 | .fake/ 311 | 312 | # CodeRush personal settings 313 | .cr/personal 314 | 315 | # Python Tools for Visual Studio (PTVS) 316 | __pycache__/ 317 | *.pyc 318 | 319 | # Cake - Uncomment if you are using it 320 | # tools/** 321 | # !tools/packages.config 322 | 323 | # Tabs Studio 324 | *.tss 325 | 326 | # Telerik's JustMock configuration file 327 | *.jmconfig 328 | 329 | # BizTalk build output 330 | *.btp.cs 331 | *.btm.cs 332 | *.odx.cs 333 | *.xsd.cs 334 | 335 | # OpenCover UI analysis results 336 | OpenCover/ 337 | 338 | # Azure Stream Analytics local run output 339 | ASALocalRun/ 340 | 341 | # MSBuild Binary and Structured Log 342 | *.binlog 343 | 344 | # NVidia Nsight GPU debugger configuration file 345 | *.nvuser 346 | 347 | # MFractors (Xamarin productivity tool) working folder 348 | .mfractor/ 349 | 350 | # Local History for Visual Studio 351 | .localhistory/ 352 | 353 | # BeatPulse healthcheck temp database 354 | healthchecksdb 355 | 356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 357 | MigrationBackup/ 358 | 359 | # Ionide (cross platform F# VS Code tools) working folder 360 | .ionide/ 361 | 362 | # Fody - auto-generated XML schema 363 | FodyWeavers.xsd -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 revit-mcp 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # revit-mcp-plugin 2 | 3 | English | [简体中文](README_zh.md) 4 | 5 | ## Introduction 6 | 7 | revit-mcp-plugin is a Revit plugin based on the MCP protocol, enabling AI to interact with Revit. 8 | 9 | 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). 10 | 11 | ## Environment Requirements 12 | 13 | - Revit 2019~2024 14 | 15 | ## Usage Instructions 16 | 17 | ### Register Plugin 18 | 19 | Register the plugin and restart Revit: 20 | 21 | ```xml 22 | 23 | 24 | 25 | revit-mcp 26 | %your_path%\revit-mcp-plugin.dll 27 | revit_mcp_plugin.Core.Application 28 | 090A4C8C-61DC-426D-87DF-E4BAE0F80EC1 29 | revit-mcp 30 | https://github.com/revit-mcp/revit-mcp-plugin 31 | 32 | 33 | ``` 34 | 35 | `%your_path%` needs to be replaced with the actual path after compilation. 36 | 37 | ### Configure Commands 38 | 39 | Add-in Modules -> Revit MCP Plugin -> Settings 40 | 41 | 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: 42 | 43 | ``` 44 | CommandSetName/ 45 | ├── 2019/ # Compatible executable files for different versions 46 | ├── 2020/ 47 | ├── 2021/ 48 | ├── 2022/ 49 | ├── 2023/ 50 | ├── 2024/ 51 | └── command.json # Configuration file 52 | ``` 53 | 54 | Successfully identified commands need to be checked to be loaded and used. 55 | 56 | ### Enable Service 57 | 58 | Add-in -> Revit MCP Plugin -> Revit MCP Switch 59 | 60 | Open the service to allow AI to discover your Revit program. Now AI can control your Revit! 61 | 62 | > 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. 63 | 64 | ## Custom Commands 65 | 66 | You can refer to the [revit-mcp-commandset](https://github.com/revit-mcp/revit-mcp-commandset) project to develop custom commands. 67 | 68 | ## Project File Organization 69 | 70 | ``` 71 | revit-mcp-plugin/ 72 | ├── Configuration/ # Configuration management related classes 73 | │ ├── CommandConfig.cs # Command configuration 74 | │ ├── ConfigurationManager.cs # Configuration manager 75 | │ ├── DeveloperInfo.cs # Developer information 76 | │ ├── FrameworkConfig.cs # Framework configuration 77 | │ └── ServiceSettings.cs # Service settings 78 | │ 79 | ├── Core/ # Program entry and core functionality 80 | │ ├── Application.cs # Application entry point 81 | │ ├── CommandExecutor.cs # Command executor 82 | │ ├── CommandManager.cs # Command manager 83 | │ ├── ExternalEventManager.cs # External event manager 84 | │ ├── MCPServiceConnection.cs # MCP service connection 85 | │ ├── RevitCommandRegistry.cs # Revit command registration 86 | │ ├── Settings.cs # Application settings 87 | │ └── SocketService.cs # Socket service implementation 88 | │ 89 | ├── Models/ # Data models 90 | │ └── ... # Various data model classes 91 | │ 92 | ├── UI/ # WPF form interfaces 93 | │ └── ... # Interface related classes 94 | │ 95 | └── Utils/ # Utility classes 96 | ├── Logger.cs # Logging utility 97 | └── PathManager.cs # Path management utility 98 | ``` 99 | 100 | ### Configuration Directory 101 | Responsible for managing various configuration information for the plugin: 102 | 103 | - CommandConfig.cs: Defines command-related configuration 104 | - ConfigurationManager.cs: Manages loading, saving, and accessing configurations 105 | - DeveloperInfo.cs: Stores developer-related information 106 | - FrameworkConfig.cs: Framework-level configuration settings 107 | - ServiceSettings.cs: Service-related settings 108 | 109 | ### Core Directory 110 | Contains the core functionality and entry point of the plugin: 111 | 112 | - Application.cs: Application entry point, responsible for initializing the plugin 113 | - CommandExecutor.cs: Core component responsible for executing Revit commands 114 | - CommandManager.cs: Manages and dispatches various commands in the plugin 115 | - ExternalEventManager.cs: Manages Revit external events 116 | - MCPServiceConnection.cs: MCP service connection 117 | - RevitCommandRegistry.cs: Registers and manages available Revit commands 118 | - Settings.cs: Triggers the display of the settings interface 119 | - SocketService.cs: Implements Socket communication with external clients 120 | 121 | ### Models Directory 122 | Contains data model classes used to pass data between different parts of the system. 123 | 124 | ### UI Directory 125 | Contains user interface related components of the plugin, implemented using the WPF framework. 126 | 127 | ### Utils Directory 128 | Provides various auxiliary tools: 129 | 130 | - Logger.cs: Logging tool for debugging and error tracking 131 | - PathManager.cs: Project-related file path management 132 | -------------------------------------------------------------------------------- /README_zh.md: -------------------------------------------------------------------------------- 1 | # revit-mcp-plugin 2 | 3 | [English](README.md) | 简体中文 4 | 5 | ## 简介 6 | 7 | revit-mcp-plugin 是一个revit插件,基于 MCP 协议制作,从而使AI可以对 Revit 进行交互。 8 | 9 | 本项目是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)(具体的功能实现)使用。 10 | 11 | ## 环境要求 12 | 13 | - revit 2019~2024 14 | 15 | ## 使用方法 16 | 17 | ### 注册插件 18 | 19 | 注册插件,重启Revit 20 | 21 | ``` 22 | 23 | 24 | 25 | revit-mcp 26 | %your_path%\revit-mcp-plugin.dll 27 | revit_mcp_plugin.Core.Application 28 | 090A4C8C-61DC-426D-87DF-E4BAE0F80EC1 29 | revit-mcp 30 | https://github.com/revit-mcp/revit-mcp-plugin 31 | 32 | 33 | ``` 34 | 35 | `%your_path%`需要替换为实际编译后的路径 36 | 37 | ### 配置命令 38 | 39 | 附加模块->Revit MCP Plugin->Settings 40 | 41 | 这个界面用于配置需要装载到revit中的命令,点击OpenCommandSetFolder打开存放命令集的文件夹,一个典型的命令集文件夹结构是这样的 42 | 43 | ``` 44 | 命令集名称/ 45 | ├── 2019/ # 兼容不同版本的执行文件 46 | ├── 2020/ 47 | ├── 2021/ 48 | ├── 2022/ 49 | ├── 2023/ 50 | ├── 2024/ 51 | └── command.json # 配置文件 52 | ``` 53 | 54 | 成功被识别的命令被勾选后,才会被加载和使用 55 | 56 | ### 启用服务 57 | 58 | 附加模块->Revit MCP Plugin->Revit MCP Switch 59 | 60 | 打开服务,让ai可以发现你的revit程序,现在ai可以操控你的revit了! 61 | 62 | > 注意:如果启用服务后,修改了配置的命令,可能需要重启REVIT才能使配置生效,这与是否命令已注册相关 63 | 64 | ## 自定义命令 65 | 66 | 可以参考[revit-mcp-commandset](https://github.com/revit-mcp/revit-mcp-commandset)项目,开发自定义命令 67 | 68 | ## 项目文件组织结构 69 | 70 | ``` 71 | revit-mcp-plugin/ 72 | ├── Configuration/ # 配置管理相关类 73 | │ ├── CommandConfig.cs # 命令配置 74 | │ ├── ConfigurationManager.cs # 配置管理器 75 | │ ├── DeveloperInfo.cs # 开发者信息 76 | │ ├── FrameworkConfig.cs # 框架配置 77 | │ └── ServiceSettings.cs # 服务设置 78 | │ 79 | ├── Core/ # 程序入口和核心功能 80 | │ ├── Application.cs # 应用程序入口点 81 | │ ├── CommandExecutor.cs # 命令执行器 82 | │ ├── CommandManager.cs # 命令管理器 83 | │ ├── ExternalEventManager.cs # 外部事件管理器 84 | │ ├── MCPServiceConnection.cs # MCP服务连接 85 | │ ├── RevitCommandRegistry.cs # Revit命令注册 86 | │ ├── Settings.cs # 应用程序设置 87 | │ └── SocketService.cs # Socket服务实现 88 | │ 89 | ├── Models/ # 数据模型 90 | │ └── ... # 各种数据模型类 91 | │ 92 | ├── UI/ # WPF窗体界面 93 | │ └── ... # 界面相关类 94 | │ 95 | └── Utils/ # 工具类 96 | ├── Logger.cs # 日志工具 97 | └── PathManager.cs # 路径管理工具 98 | ``` 99 | 100 | ### Configuration 目录 101 | 负责管理插件的各种配置信息: 102 | 103 | - CommandConfig.cs: 定义命令相关配置 104 | - ConfigurationManager.cs: 管理配置的加载、保存和访问 105 | - DeveloperInfo.cs: 存储开发者相关信息 106 | - FrameworkConfig.cs: 框架级别的配置设置 107 | - ServiceSettings.cs: 服务相关设置 108 | 109 | ### Core 目录 110 | 包含插件的核心功能和入口点: 111 | 112 | - Application.cs: 应用程序入口点,负责初始化插件 113 | - CommandExecutor.cs: 负责执行Revit命令的核心组件 114 | - CommandManager.cs: 管理和调度插件中的各种命令 115 | - ExternalEventManager.cs: 管理Revit外部事件 116 | - MCPServiceConnection.cs: MCP服务连接 117 | - RevitCommandRegistry.cs: 注册和管理可用的Revit命令 118 | - Settings.cs: 触发显示设置界面 119 | - SocketService.cs: 实现与外部客户端的Socket通信 120 | 121 | ### Models 目录 122 | 包含数据模型类,用于在系统各部分之间传递数据。 123 | 124 | ### UI 目录 125 | 包含插件的用户界面相关组件,使用WPF框架实现。 126 | 127 | ### Utils 目录 128 | 提供各种辅助工具: 129 | 130 | - Logger.cs: 日志记录工具,用于调试和错误追踪 131 | - PathManager.cs: 项目相关文件路径管理 132 | -------------------------------------------------------------------------------- /SampleCommandSet/Commands/Access/GetAvailableFamilyTypesCommand.cs: -------------------------------------------------------------------------------- 1 | using Autodesk.Revit.UI; 2 | using Newtonsoft.Json.Linq; 3 | using revit_mcp_sdk.API.Base; 4 | using System; 5 | using System.Collections.Generic; 6 | 7 | namespace SampleCommandSet.Commands.Access 8 | { 9 | /// 10 | /// 获取当前项目中可用族类型的命令 11 | /// 12 | public class GetAvailableFamilyTypesCommand : ExternalEventCommandBase 13 | { 14 | private GetAvailableFamilyTypesEventHandler _handler => (GetAvailableFamilyTypesEventHandler)Handler; 15 | 16 | public override string CommandName => "get_available_family_types"; 17 | 18 | public GetAvailableFamilyTypesCommand(UIApplication uiApp) 19 | : base(new GetAvailableFamilyTypesEventHandler(), uiApp) 20 | { 21 | } 22 | 23 | public override object Execute(JObject parameters, string requestId) 24 | { 25 | try 26 | { 27 | // 解析参数 28 | List categoryList = parameters?["categoryList"]?.ToObject>() ?? new List(); 29 | string familyNameFilter = parameters?["familyNameFilter"]?.Value(); 30 | int? limit = parameters?["limit"]?.Value(); 31 | 32 | // 设置查询参数 33 | _handler.CategoryList = categoryList; 34 | _handler.FamilyNameFilter = familyNameFilter; 35 | _handler.Limit = limit; 36 | 37 | // 触发外部事件并等待完成,最多等待15秒 38 | if (RaiseAndWaitForCompletion(15000)) 39 | { 40 | return _handler.ResultFamilyTypes; 41 | } 42 | else 43 | { 44 | throw new TimeoutException("获取可用族类型超时"); 45 | } 46 | } 47 | catch (Exception ex) 48 | { 49 | throw new Exception($"获取可用族类型失败: {ex.Message}"); 50 | } 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /SampleCommandSet/Commands/Access/GetAvailableFamilyTypesEventHandler.cs: -------------------------------------------------------------------------------- 1 | using Autodesk.Revit.DB; 2 | using Autodesk.Revit.UI; 3 | using revit_mcp_sdk.API.Interfaces; 4 | using SampleCommandSet.Extensions; 5 | using SampleCommandSet.Models; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Threading; 10 | 11 | namespace SampleCommandSet.Commands.Access 12 | { 13 | public class GetAvailableFamilyTypesEventHandler : IExternalEventHandler, IWaitableExternalEventHandler 14 | { 15 | // 执行结果 16 | public List ResultFamilyTypes { get; private set; } 17 | 18 | // 状态同步对象 19 | public bool TaskCompleted { get; private set; } 20 | private readonly ManualResetEvent _resetEvent = new ManualResetEvent(false); 21 | 22 | // 过滤条件 23 | public List CategoryList { get; set; } 24 | public string FamilyNameFilter { get; set; } 25 | public int? Limit { get; set; } 26 | 27 | // 执行时间,略微比调用超时更短一些 28 | public bool WaitForCompletion(int timeoutMilliseconds = 12500) 29 | { 30 | return _resetEvent.WaitOne(timeoutMilliseconds); 31 | } 32 | 33 | public void Execute(UIApplication app) 34 | { 35 | try 36 | { 37 | var doc = app.ActiveUIDocument.Document; 38 | 39 | // 获取所有族符号(可载入族) 40 | var familySymbols = new FilteredElementCollector(doc) 41 | .OfClass(typeof(FamilySymbol)) 42 | .Cast(); 43 | // 获取系统族类型(墙、楼板等) 44 | var systemTypes = new List(); 45 | systemTypes.AddRange(new FilteredElementCollector(doc).OfClass(typeof(WallType)).Cast()); 46 | systemTypes.AddRange(new FilteredElementCollector(doc).OfClass(typeof(FloorType)).Cast()); 47 | systemTypes.AddRange(new FilteredElementCollector(doc).OfClass(typeof(RoofType)).Cast()); 48 | systemTypes.AddRange(new FilteredElementCollector(doc).OfClass(typeof(CurtainSystemType)).Cast()); 49 | // 合并结果 50 | var allElements = familySymbols 51 | .Cast() 52 | .Concat(systemTypes) 53 | .ToList(); 54 | 55 | IEnumerable filteredElements = allElements; 56 | 57 | // 类别过滤 58 | if (CategoryList != null && CategoryList.Any()) 59 | { 60 | var validCategoryIds = new List(); 61 | foreach (var categoryName in CategoryList) 62 | { 63 | if (Enum.TryParse(categoryName, out BuiltInCategory bic)) 64 | { 65 | validCategoryIds.Add((int)bic); 66 | } 67 | } 68 | 69 | if (validCategoryIds.Any()) 70 | { 71 | filteredElements = filteredElements.Where(et => 72 | { 73 | var categoryId = et.Category?.Id.GetIdValue(); 74 | return categoryId != null && validCategoryIds.Contains((int)categoryId.Value); 75 | }); 76 | } 77 | } 78 | 79 | // 名称模糊匹配(同时匹配族名和类型名) 80 | if (!string.IsNullOrEmpty(FamilyNameFilter)) 81 | { 82 | filteredElements = filteredElements.Where(et => 83 | { 84 | string familyName = (et is FamilySymbol fs) ? fs.FamilyName : et.get_Parameter( 85 | BuiltInParameter.SYMBOL_FAMILY_NAME_PARAM)?.AsString() ?? ""; 86 | 87 | return (familyName?.IndexOf(FamilyNameFilter, StringComparison.OrdinalIgnoreCase) >= 0 || 88 | et.Name.IndexOf(FamilyNameFilter, StringComparison.OrdinalIgnoreCase) >= 0); 89 | }); 90 | } 91 | 92 | // 限制返回数量 93 | if (Limit.HasValue && Limit.Value > 0) 94 | { 95 | filteredElements = filteredElements.Take(Limit.Value); 96 | } 97 | 98 | // 转换为FamilyTypeInfo列表 99 | ResultFamilyTypes = filteredElements.Select(et => 100 | { 101 | string familyName; 102 | if (et is FamilySymbol fs) 103 | { 104 | familyName = fs.FamilyName; 105 | } 106 | else 107 | { 108 | Parameter param = et.get_Parameter(BuiltInParameter.SYMBOL_FAMILY_NAME_PARAM); 109 | familyName = param?.AsString() ?? et.GetType().Name.Replace("Type", ""); 110 | } 111 | return new FamilyTypeInfo 112 | { 113 | FamilyTypeId = et.Id.GetIdValue(), 114 | UniqueId = et.UniqueId, 115 | FamilyName = familyName, 116 | TypeName = et.Name, 117 | Category = et.Category?.Name 118 | }; 119 | }).ToList(); 120 | } 121 | catch (Exception ex) 122 | { 123 | TaskDialog.Show("Error", "获取族类型失败: " + ex.Message); 124 | } 125 | finally 126 | { 127 | TaskCompleted = true; 128 | _resetEvent.Set(); 129 | } 130 | } 131 | 132 | public string GetName() 133 | { 134 | return "获取可用族类型"; 135 | } 136 | } 137 | } -------------------------------------------------------------------------------- /SampleCommandSet/Commands/Access/GetCurrentViewElementsCommand.cs: -------------------------------------------------------------------------------- 1 | using Autodesk.Revit.UI; 2 | using Newtonsoft.Json.Linq; 3 | using revit_mcp_sdk.API.Base; 4 | using System; 5 | using System.Collections.Generic; 6 | 7 | namespace SampleCommandSet.Commands.Access 8 | { 9 | /// 10 | /// 获取当前视图元素的命令 11 | /// 12 | public class GetCurrentViewElementsCommand : ExternalEventCommandBase 13 | { 14 | private GetCurrentViewElementsEventHandler _handler => (GetCurrentViewElementsEventHandler)Handler; 15 | 16 | public override string CommandName => "get_current_view_elements"; 17 | 18 | public GetCurrentViewElementsCommand(UIApplication uiApp) 19 | : base(new GetCurrentViewElementsEventHandler(), uiApp) 20 | { 21 | } 22 | 23 | public override object Execute(JObject parameters, string requestId) 24 | { 25 | try 26 | { 27 | // 解析参数 28 | List modelCategoryList = parameters?["modelCategoryList"]?.ToObject>() ?? new List(); 29 | List annotationCategoryList = parameters?["annotationCategoryList"]?.ToObject>() ?? new List(); 30 | bool includeHidden = parameters?["includeHidden"]?.Value() ?? false; 31 | int limit = parameters?["limit"]?.Value() ?? 100; 32 | 33 | // 设置查询参数 34 | _handler.SetQueryParameters(modelCategoryList, annotationCategoryList, includeHidden, limit); 35 | 36 | // 触发外部事件并等待完成 37 | if (RaiseAndWaitForCompletion(60000)) // 60秒超时 38 | { 39 | return _handler.ResultInfo; 40 | } 41 | else 42 | { 43 | throw new TimeoutException("获取视图元素超时"); 44 | } 45 | } 46 | catch (Exception ex) 47 | { 48 | throw new Exception($"获取视图元素失败: {ex.Message}"); 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /SampleCommandSet/Commands/Access/GetCurrentViewElementsEventHandler.cs: -------------------------------------------------------------------------------- 1 | using Autodesk.Revit.DB; 2 | using Autodesk.Revit.UI; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading; 7 | using SampleCommandSet.Extensions; 8 | using revit_mcp_sdk.API.Interfaces; 9 | 10 | namespace SampleCommandSet.Commands.Access 11 | { 12 | /// 13 | /// 获取当前视图元素的事件处理器 14 | /// 15 | public class GetCurrentViewElementsEventHandler : IExternalEventHandler, IWaitableExternalEventHandler 16 | { 17 | // 默认模型类别列表 18 | private readonly List _defaultModelCategories = new List 19 | { 20 | "OST_Walls", 21 | "OST_Doors", 22 | "OST_Windows", 23 | "OST_Furniture", 24 | "OST_Columns", 25 | "OST_Floors", 26 | "OST_Roofs", 27 | "OST_Stairs", 28 | "OST_StructuralFraming", 29 | "OST_Ceilings", 30 | "OST_MEPSpaces", 31 | "OST_Rooms" 32 | }; 33 | // 默认注释类别列表 34 | private readonly List _defaultAnnotationCategories = new List 35 | { 36 | "OST_Dimensions", 37 | "OST_TextNotes", 38 | "OST_GenericAnnotation", 39 | "OST_WallTags", 40 | "OST_DoorTags", 41 | "OST_WindowTags", 42 | "OST_RoomTags", 43 | "OST_AreaTags", 44 | "OST_SpaceTags", 45 | "OST_ViewportLabels", 46 | "OST_TitleBlocks" 47 | }; 48 | 49 | // 查询参数 50 | private List _modelCategoryList; 51 | private List _annotationCategoryList; 52 | private bool _includeHidden; 53 | private int _limit; 54 | 55 | // 执行结果 56 | public ViewElementsResult ResultInfo { get; private set; } 57 | 58 | // 状态同步对象 59 | public bool TaskCompleted { get; private set; } 60 | private readonly ManualResetEvent _resetEvent = new ManualResetEvent(false); 61 | 62 | // 设置查询参数 63 | public void SetQueryParameters(List modelCategoryList, List annotationCategoryList, bool includeHidden, int limit) 64 | { 65 | _modelCategoryList = modelCategoryList; 66 | _annotationCategoryList = annotationCategoryList; 67 | _includeHidden = includeHidden; 68 | _limit = limit; 69 | TaskCompleted = false; 70 | _resetEvent.Reset(); 71 | } 72 | 73 | // 实现IWaitableExternalEventHandler接口 74 | public bool WaitForCompletion(int timeoutMilliseconds = 10000) 75 | { 76 | return _resetEvent.WaitOne(timeoutMilliseconds); 77 | } 78 | 79 | public void Execute(UIApplication app) 80 | { 81 | try 82 | { 83 | var uiDoc = app.ActiveUIDocument; 84 | var doc = uiDoc.Document; 85 | var activeView = doc.ActiveView; 86 | 87 | // 如果传入的类别列表为空,则使用默认列表 88 | List modelCategories = (_modelCategoryList == null || _modelCategoryList.Count == 0) 89 | ? _defaultModelCategories 90 | : _modelCategoryList; 91 | 92 | List annotationCategories = (_annotationCategoryList == null || _annotationCategoryList.Count == 0) 93 | ? _defaultAnnotationCategories 94 | : _annotationCategoryList; 95 | 96 | // 合并所有类别 97 | List allCategories = new List(); 98 | allCategories.AddRange(modelCategories); 99 | allCategories.AddRange(annotationCategories); 100 | 101 | // 获取当前视图中的所有元素 102 | var collector = new FilteredElementCollector(doc, activeView.Id) 103 | .WhereElementIsNotElementType(); 104 | 105 | // 获取所有元素 106 | IList elements = collector.ToElements(); 107 | 108 | // 按类别筛选 109 | if (allCategories.Count > 0) 110 | { 111 | // 转换字符串类别为枚举 112 | List builtInCategories = new List(); 113 | foreach (string categoryName in allCategories) 114 | { 115 | if (Enum.TryParse(categoryName, out BuiltInCategory category)) 116 | { 117 | builtInCategories.Add(category); 118 | } 119 | } 120 | // 如果成功解析了类别,则使用类别过滤器 121 | if (builtInCategories.Count > 0) 122 | { 123 | ElementMulticategoryFilter categoryFilter = new ElementMulticategoryFilter(builtInCategories); 124 | elements = new FilteredElementCollector(doc, activeView.Id) 125 | .WhereElementIsNotElementType() 126 | .WherePasses(categoryFilter) 127 | .ToElements(); 128 | } 129 | } 130 | 131 | // 过滤隐藏的元素 132 | if (!_includeHidden) 133 | { 134 | elements = elements.Where(e => !e.IsHidden(activeView)).ToList(); 135 | } 136 | 137 | // 限制返回数量 138 | if (_limit > 0 && elements.Count > _limit) 139 | { 140 | elements = elements.Take(_limit).ToList(); 141 | } 142 | 143 | // 构建结果 144 | var elementInfos = elements.Select(e => new ElementInfo 145 | { 146 | Id = e.Id.GetIdValue(), 147 | UniqueId = e.UniqueId, 148 | Name = e.Name, 149 | Category = e.Category?.Name ?? "unknow", 150 | Properties = GetElementProperties(e) 151 | }).ToList(); 152 | 153 | ResultInfo = new ViewElementsResult 154 | { 155 | ViewId = activeView.Id.GetIdValue(), 156 | ViewName = activeView.Name, 157 | TotalElementsInView = new FilteredElementCollector(doc, activeView.Id).GetElementCount(), 158 | FilteredElementCount = elementInfos.Count, 159 | Elements = elementInfos 160 | }; 161 | } 162 | catch (Exception ex) 163 | { 164 | TaskDialog.Show("error", ex.Message); 165 | } 166 | finally 167 | { 168 | TaskCompleted = true; 169 | _resetEvent.Set(); 170 | } 171 | } 172 | 173 | private Dictionary GetElementProperties(Element element) 174 | { 175 | var properties = new Dictionary(); 176 | 177 | // 添加通用属性 178 | properties.Add("ElementId", element.Id.GetIdValue().ToString()); 179 | 180 | if (element.Location != null) 181 | { 182 | if (element.Location is LocationPoint locationPoint) 183 | { 184 | var point = locationPoint.Point; 185 | properties.Add("LocationX", point.X.ToString("F2")); 186 | properties.Add("LocationY", point.Y.ToString("F2")); 187 | properties.Add("LocationZ", point.Z.ToString("F2")); 188 | } 189 | else if (element.Location is LocationCurve locationCurve) 190 | { 191 | var curve = locationCurve.Curve; 192 | properties.Add("Start", $"{curve.GetEndPoint(0).X:F2}, {curve.GetEndPoint(0).Y:F2}, {curve.GetEndPoint(0).Z:F2}"); 193 | properties.Add("End", $"{curve.GetEndPoint(1).X:F2}, {curve.GetEndPoint(1).Y:F2}, {curve.GetEndPoint(1).Z:F2}"); 194 | properties.Add("Length", curve.Length.ToString("F2")); 195 | } 196 | } 197 | 198 | // 获取常用参数值 199 | var commonParams = new[] { "Comments", "Mark", "Level", "Family", "Type" }; 200 | foreach (var paramName in commonParams) 201 | { 202 | Parameter param = element.LookupParameter(paramName); 203 | if (param != null && !param.IsReadOnly) 204 | { 205 | if (param.StorageType == StorageType.String) 206 | properties.Add(paramName, param.AsString() ?? ""); 207 | else if (param.StorageType == StorageType.Double) 208 | properties.Add(paramName, param.AsDouble().ToString("F2")); 209 | else if (param.StorageType == StorageType.Integer) 210 | properties.Add(paramName, param.AsInteger().ToString()); 211 | else if (param.StorageType == StorageType.ElementId) 212 | properties.Add(paramName, param.AsElementId().GetIdValue().ToString()); 213 | } 214 | } 215 | 216 | return properties; 217 | } 218 | 219 | public string GetName() 220 | { 221 | return "获取当前视图元素"; 222 | } 223 | } 224 | 225 | /// 226 | /// 元素信息数据结构 227 | /// 228 | public class ElementInfo 229 | { 230 | public long Id { get; set; } 231 | public string UniqueId { get; set; } 232 | public string Name { get; set; } 233 | public string Category { get; set; } 234 | public Dictionary Properties { get; set; } = new Dictionary(); 235 | } 236 | 237 | /// 238 | /// 视图元素结果数据结构 239 | /// 240 | public class ViewElementsResult 241 | { 242 | public long ViewId { get; set; } 243 | public string ViewName { get; set; } 244 | public int TotalElementsInView { get; set; } 245 | public int FilteredElementCount { get; set; } 246 | public List Elements { get; set; } = new List(); 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /SampleCommandSet/Commands/Access/GetCurrentViewInfoCommand.cs: -------------------------------------------------------------------------------- 1 | using Autodesk.Revit.UI; 2 | using Newtonsoft.Json.Linq; 3 | using revit_mcp_sdk.API.Base; 4 | using System; 5 | 6 | namespace SampleCommandSet.Commands.Access 7 | { 8 | public class GetCurrentViewInfoCommand : ExternalEventCommandBase 9 | { 10 | private GetCurrentViewInfoEventHandler _handler => (GetCurrentViewInfoEventHandler)Handler; 11 | 12 | public override string CommandName => "get_current_view_info"; 13 | 14 | public GetCurrentViewInfoCommand(UIApplication uiApp) 15 | : base(new GetCurrentViewInfoEventHandler(), uiApp) 16 | { 17 | } 18 | 19 | public override object Execute(JObject parameters, string requestId) 20 | { 21 | // 触发外部事件并等待完成 22 | if (RaiseAndWaitForCompletion(10000)) // 10秒超时 23 | { 24 | return _handler.ResultInfo; 25 | } 26 | else 27 | { 28 | throw new TimeoutException("获取信息超时"); 29 | } 30 | } 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /SampleCommandSet/Commands/Access/GetCurrentViewInfoEventHandler.cs: -------------------------------------------------------------------------------- 1 | using Autodesk.Revit.UI; 2 | using revit_mcp_sdk.API.Interfaces; 3 | using System; 4 | using System.Threading; 5 | using SampleCommandSet.Extensions; 6 | using SampleCommandSet.Models; 7 | 8 | namespace SampleCommandSet.Commands.Access 9 | { 10 | public class GetCurrentViewInfoEventHandler: IExternalEventHandler, IWaitableExternalEventHandler 11 | { 12 | // 执行结果 13 | public ViewInfo ResultInfo { get; private set; } 14 | 15 | // 状态同步对象 16 | public bool TaskCompleted { get; private set; } 17 | private readonly ManualResetEvent _resetEvent = new ManualResetEvent(false); 18 | 19 | // 实现IWaitableExternalEventHandler接口 20 | public bool WaitForCompletion(int timeoutMilliseconds = 10000) 21 | { 22 | return _resetEvent.WaitOne(timeoutMilliseconds); 23 | } 24 | 25 | public void Execute(UIApplication app) 26 | { 27 | try 28 | { 29 | var uiDoc = app.ActiveUIDocument; 30 | var doc = uiDoc.Document; 31 | var activeView = doc.ActiveView; 32 | 33 | ResultInfo = new ViewInfo 34 | { 35 | Id = activeView.Id.GetIdValue(), 36 | UniqueId = activeView.UniqueId, 37 | Name = activeView.Name, 38 | ViewType = activeView.ViewType.ToString(), 39 | IsTemplate = activeView.IsTemplate, 40 | Scale = activeView.Scale, 41 | DetailLevel = activeView.DetailLevel.ToString(), 42 | }; 43 | } 44 | catch (Exception ex) 45 | { 46 | TaskDialog.Show("error", "获取信息失败"); 47 | } 48 | finally 49 | { 50 | TaskCompleted = true; 51 | _resetEvent.Set(); 52 | } 53 | } 54 | 55 | public string GetName() 56 | { 57 | return "获取当前视图信息"; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /SampleCommandSet/Commands/Access/GetSelectedElementsCommand.cs: -------------------------------------------------------------------------------- 1 | using Autodesk.Revit.UI; 2 | using Newtonsoft.Json.Linq; 3 | using revit_mcp_sdk.API.Base; 4 | using System; 5 | 6 | namespace SampleCommandSet.Commands.Access 7 | { 8 | /// 9 | /// 获取当前选中元素的命令 10 | /// 11 | public class GetSelectedElementsCommand : ExternalEventCommandBase 12 | { 13 | private GetSelectedElementsEventHandler _handler => (GetSelectedElementsEventHandler)Handler; 14 | 15 | public override string CommandName => "get_selected_elements"; 16 | 17 | public GetSelectedElementsCommand(UIApplication uiApp) 18 | : base(new GetSelectedElementsEventHandler(), uiApp) 19 | { 20 | } 21 | 22 | public override object Execute(JObject parameters, string requestId) 23 | { 24 | try 25 | { 26 | // 解析参数 27 | int? limit = parameters?["limit"]?.Value(); 28 | 29 | // 设置数量限制 30 | _handler.Limit = limit; 31 | 32 | // 触发外部事件并等待完成 33 | if (RaiseAndWaitForCompletion(15000)) 34 | { 35 | return _handler.ResultElements; 36 | } 37 | else 38 | { 39 | throw new TimeoutException("获取选中元素超时"); 40 | } 41 | } 42 | catch (Exception ex) 43 | { 44 | throw new Exception($"获取选中元素失败: {ex.Message}"); 45 | } 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /SampleCommandSet/Commands/Access/GetSelectedElementsEventHandler.cs: -------------------------------------------------------------------------------- 1 | using Autodesk.Revit.DB; 2 | using Autodesk.Revit.UI; 3 | using revit_mcp_sdk.API.Interfaces; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Threading; 8 | using SampleCommandSet.Extensions; 9 | 10 | namespace SampleCommandSet.Commands.Access 11 | { 12 | public class GetSelectedElementsEventHandler : IExternalEventHandler, IWaitableExternalEventHandler 13 | { 14 | // 执行结果 15 | public List ResultElements { get; private set; } 16 | 17 | // 状态同步对象 18 | public bool TaskCompleted { get; private set; } 19 | private readonly ManualResetEvent _resetEvent = new ManualResetEvent(false); 20 | 21 | // 限制返回的元素数量 22 | public int? Limit { get; set; } 23 | 24 | // 实现IWaitableExternalEventHandler接口 25 | public bool WaitForCompletion(int timeoutMilliseconds = 10000) 26 | { 27 | return _resetEvent.WaitOne(timeoutMilliseconds); 28 | } 29 | 30 | public void Execute(UIApplication app) 31 | { 32 | try 33 | { 34 | var uiDoc = app.ActiveUIDocument; 35 | var doc = uiDoc.Document; 36 | 37 | // 获取当前选中的元素 38 | var selectedIds = uiDoc.Selection.GetElementIds(); 39 | var selectedElements = selectedIds.Select(id => doc.GetElement(id)).ToList(); 40 | 41 | // 应用数量限制 42 | if (Limit.HasValue && Limit.Value > 0) 43 | { 44 | selectedElements = selectedElements.Take(Limit.Value).ToList(); 45 | } 46 | 47 | // 转换为ElementInfo列表 48 | ResultElements = selectedElements.Select(element => new ElementInfo 49 | { 50 | Id = element.Id.GetIdValue(), 51 | UniqueId = element.UniqueId, 52 | Name = element.Name, 53 | Category = element.Category?.Name 54 | }).ToList(); 55 | } 56 | catch (Exception ex) 57 | { 58 | TaskDialog.Show("Error", "获取选中元素失败: " + ex.Message); 59 | ResultElements = new List(); 60 | } 61 | finally 62 | { 63 | TaskCompleted = true; 64 | _resetEvent.Set(); 65 | } 66 | } 67 | 68 | public string GetName() 69 | { 70 | return "获取选中元素"; 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /SampleCommandSet/Commands/Create/CreateWallCommand.cs: -------------------------------------------------------------------------------- 1 | using Autodesk.Revit.UI; 2 | using revit_mcp_sdk.API.Base; 3 | using System; 4 | using Newtonsoft.Json.Linq; 5 | 6 | namespace SampleCommandSet.Commands.Create 7 | { 8 | /// 9 | /// 创建墙命令 10 | /// 11 | public class CreateWallCommand : ExternalEventCommandBase 12 | { 13 | private CreateWallEventHandler _handler => (CreateWallEventHandler)Handler; 14 | 15 | /// 16 | /// 命令名称 17 | /// 18 | public override string CommandName => "create_Wall"; 19 | 20 | /// 21 | /// 构造函数 22 | /// 23 | /// Revit UIApplication 24 | public CreateWallCommand(UIApplication uiApp) 25 | : base(new CreateWallEventHandler(), uiApp) 26 | { 27 | } 28 | 29 | /// 30 | /// 执行命令 31 | /// 32 | /// JSON参数 33 | /// 请求ID 34 | /// 命令执行结果 35 | public override object Execute(JObject parameters, string requestId) 36 | { 37 | try 38 | { 39 | // 解析墙参数 40 | double startX = parameters["startX"].Value(); 41 | double startY = parameters["startY"].Value(); 42 | double endX = parameters["endX"].Value(); 43 | double endY = parameters["endY"].Value(); 44 | double height = parameters["height"].Value(); 45 | double thickness = parameters["thickness"].Value(); 46 | 47 | // 设置墙体参数 48 | _handler.SetWallParameters(startX, startY, endX, endY, height, thickness); 49 | 50 | // 触发外部事件并等待完成 51 | if (RaiseAndWaitForCompletion(10000)) 52 | { 53 | return _handler.CreatedWallInfo; 54 | } 55 | else 56 | { 57 | throw new TimeoutException("创建墙体操作超时"); 58 | } 59 | } 60 | catch (Exception ex) 61 | { 62 | throw new Exception($"创建墙体失败: {ex.Message}"); 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /SampleCommandSet/Commands/Create/CreateWallEventHandler.cs: -------------------------------------------------------------------------------- 1 | using Autodesk.Revit.DB; 2 | using Autodesk.Revit.UI; 3 | using revit_mcp_sdk.API.Interfaces; 4 | using SampleCommandSet.Models; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | 12 | namespace SampleCommandSet.Commands.Create 13 | { 14 | /// 15 | /// 创建墙的外部事件处理器 16 | /// 17 | public class CreateWallEventHandler : IExternalEventHandler, IWaitableExternalEventHandler 18 | { 19 | // 创建墙的参数 20 | private double _startX; 21 | private double _startY; 22 | private double _endX; 23 | private double _endY; 24 | private double _height; 25 | private double _thickness; 26 | 27 | // 创建的墙体信息 28 | private Wall _createdWall; 29 | public WallInfo CreatedWallInfo { get; private set; } 30 | 31 | // 标记操作是否完成 32 | private bool _taskCompleted; 33 | 34 | // 事件等待对象 35 | private readonly ManualResetEvent _resetEvent = new ManualResetEvent(false); 36 | 37 | /// 38 | /// 设置创建墙的参数 39 | /// 40 | public void SetWallParameters(double startX, double startY, double endX, double endY, double height, double thickness) 41 | { 42 | _startX = startX; 43 | _startY = startY; 44 | _endX = endX; 45 | _endY = endY; 46 | _height = height; 47 | _thickness = thickness; 48 | 49 | _taskCompleted = false; 50 | _resetEvent.Reset(); 51 | } 52 | 53 | /// 54 | /// 等待墙创建完成 55 | /// 56 | /// 超时时间(毫秒) 57 | /// 操作是否在超时前完成 58 | public bool WaitForCompletion(int timeoutMilliseconds = 10000) 59 | { 60 | return _resetEvent.WaitOne(timeoutMilliseconds); 61 | } 62 | 63 | /// 64 | /// IExternalEventHandler.Execute 实现 65 | /// 66 | public void Execute(UIApplication app) 67 | { 68 | try 69 | { 70 | Document doc = app.ActiveUIDocument.Document; 71 | 72 | using (Transaction trans = new Transaction(doc, "创建墙体")) 73 | { 74 | trans.Start(); 75 | 76 | // 创建墙的起点和终点 77 | XYZ startPoint = new XYZ(_startX, _startY, 0); 78 | XYZ endPoint = new XYZ(_endX, _endY, 0); 79 | 80 | // 创建墙的曲线 81 | Line curve = Line.CreateBound(startPoint, endPoint); 82 | 83 | // 获取当前文档中的墙类型 84 | FilteredElementCollector collector = new FilteredElementCollector(doc); 85 | collector.OfClass(typeof(WallType)); 86 | WallType wallType = collector.FirstOrDefault(w => w.Name.Contains("常规")) as WallType; 87 | 88 | // 创建墙 89 | _createdWall = Wall.Create( 90 | doc, 91 | curve, 92 | wallType.Id, 93 | doc.ActiveView.GenLevel.Id, 94 | _height, 95 | 0.0, // 墙基点偏移 96 | false, // 不翻转 97 | false); // 不是结构墙 98 | 99 | trans.Commit(); 100 | 101 | // 获取墙的详细信息 102 | CreatedWallInfo = new WallInfo 103 | { 104 | ElementId = _createdWall.Id.IntegerValue, 105 | StartPoint = new Models.Point { X = startPoint.X, Y = startPoint.Y, Z = 0 }, 106 | EndPoint = new Models.Point { X = endPoint.X, Y = endPoint.Y, Z = 0 }, 107 | Height = _height, 108 | Thickness = _thickness, 109 | }; 110 | } 111 | } 112 | catch (Exception ex) 113 | { 114 | TaskDialog.Show("错误", $"创建墙体时出错: {ex.Message}"); 115 | 116 | } 117 | finally 118 | { 119 | _taskCompleted = true; 120 | _resetEvent.Set(); // 通知等待线程操作已完成 121 | } 122 | } 123 | 124 | /// 125 | /// IExternalEventHandler.GetName 实现 126 | /// 127 | public string GetName() 128 | { 129 | return "创建墙体"; 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /SampleCommandSet/Commands/Delete/DeleteElementCommand.cs: -------------------------------------------------------------------------------- 1 | using Autodesk.Revit.UI; 2 | using Newtonsoft.Json.Linq; 3 | using revit_mcp_sdk.API.Base; 4 | using revit_mcp_sdk.API.Models; 5 | using revit_mcp_sdk.Exceptions; 6 | using System; 7 | 8 | namespace SampleCommandSet.Commands.Delete 9 | { 10 | public class DeleteElementCommand : ExternalEventCommandBase 11 | { 12 | private DeleteElementEventHandler _handler => (DeleteElementEventHandler)Handler; 13 | public override string CommandName => "delete_element"; 14 | public DeleteElementCommand(UIApplication uiApp) 15 | : base(new DeleteElementEventHandler(), uiApp) 16 | { 17 | } 18 | public override object Execute(JObject parameters, string requestId) 19 | { 20 | try 21 | { 22 | // 解析数组参数 23 | var elementIds = parameters?["elementIds"]?.ToObject(); 24 | if (elementIds == null || elementIds.Length == 0) 25 | { 26 | throw new CommandExecutionException( 27 | "元素ID列表不能为空", 28 | JsonRPCErrorCodes.InvalidParams); 29 | } 30 | // 设置要删除的元素ID数组 31 | _handler.ElementIds = elementIds; 32 | // 触发外部事件并等待完成 33 | if (RaiseAndWaitForCompletion(15000)) 34 | { 35 | if (_handler.IsSuccess) 36 | { 37 | return CommandResult.CreateSuccess(new { deleted = true, count = _handler.DeletedCount }); 38 | } 39 | else 40 | { 41 | throw new CommandExecutionException( 42 | "删除元素失败", 43 | JsonRPCErrorCodes.ElementDeletionFailed); 44 | } 45 | } 46 | else 47 | { 48 | throw CreateTimeoutException(CommandName); 49 | } 50 | } 51 | catch (CommandExecutionException) 52 | { 53 | throw; 54 | } 55 | catch (Exception ex) 56 | { 57 | throw new CommandExecutionException( 58 | $"删除元素失败: {ex.Message}", 59 | JsonRPCErrorCodes.InternalError); 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /SampleCommandSet/Commands/Delete/DeleteElementEventHandler.cs: -------------------------------------------------------------------------------- 1 | using Autodesk.Revit.DB; 2 | using Autodesk.Revit.UI; 3 | using revit_mcp_sdk.API.Interfaces; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Threading; 7 | 8 | namespace SampleCommandSet.Commands.Delete 9 | { 10 | public class DeleteElementEventHandler : IExternalEventHandler, IWaitableExternalEventHandler 11 | { 12 | // 执行结果 13 | public bool IsSuccess { get; private set; } 14 | 15 | // 成功删除的元素数量 16 | public int DeletedCount { get; private set; } 17 | // 状态同步对象 18 | public bool TaskCompleted { get; private set; } 19 | private readonly ManualResetEvent _resetEvent = new ManualResetEvent(false); 20 | // 要删除的元素ID数组 21 | public string[] ElementIds { get; set; } 22 | // 实现IWaitableExternalEventHandler接口 23 | public bool WaitForCompletion(int timeoutMilliseconds = 10000) 24 | { 25 | return _resetEvent.WaitOne(timeoutMilliseconds); 26 | } 27 | public void Execute(UIApplication app) 28 | { 29 | try 30 | { 31 | var doc = app.ActiveUIDocument.Document; 32 | DeletedCount = 0; 33 | if (ElementIds == null || ElementIds.Length == 0) 34 | { 35 | IsSuccess = false; 36 | return; 37 | } 38 | // 创建待删除元素ID集合 39 | List elementIdsToDelete = new List(); 40 | List invalidIds = new List(); 41 | foreach (var idStr in ElementIds) 42 | { 43 | if (int.TryParse(idStr, out int elementIdValue)) 44 | { 45 | var elementId = new ElementId(elementIdValue); 46 | // 检查元素是否存在 47 | if (doc.GetElement(elementId) != null) 48 | { 49 | elementIdsToDelete.Add(elementId); 50 | } 51 | } 52 | else 53 | { 54 | invalidIds.Add(idStr); 55 | } 56 | } 57 | if (invalidIds.Count > 0) 58 | { 59 | TaskDialog.Show("警告", $"以下ID无效或元素不存在:{string.Join(", ", invalidIds)}"); 60 | } 61 | // 如果有可删除的元素,则执行删除 62 | if (elementIdsToDelete.Count > 0) 63 | { 64 | using (var transaction = new Transaction(doc, "Delete Elements")) 65 | { 66 | transaction.Start(); 67 | 68 | // 批量删除元素 69 | ICollection deletedIds = doc.Delete(elementIdsToDelete); 70 | DeletedCount = deletedIds.Count; 71 | 72 | transaction.Commit(); 73 | } 74 | IsSuccess = true; 75 | } 76 | else 77 | { 78 | TaskDialog.Show("错误", "没有有效的元素可以删除"); 79 | IsSuccess = false; 80 | } 81 | } 82 | catch (Exception ex) 83 | { 84 | TaskDialog.Show("错误", "删除元素失败: " + ex.Message); 85 | IsSuccess = false; 86 | } 87 | finally 88 | { 89 | TaskCompleted = true; 90 | _resetEvent.Set(); 91 | } 92 | } 93 | public string GetName() 94 | { 95 | return "删除元素"; 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /SampleCommandSet/Commands/Test/SayHelloCommand.cs: -------------------------------------------------------------------------------- 1 | using Autodesk.Revit.UI; 2 | using Newtonsoft.Json.Linq; 3 | using revit_mcp_sdk.API.Base; 4 | using revit_mcp_sdk.API.Models; 5 | 6 | namespace SampleCommandSet.Commands.Test 7 | { 8 | public class SayHelloCommand : ExternalEventCommandBase 9 | { 10 | private SayHelloEventHandler _handler => (SayHelloEventHandler)Handler; 11 | 12 | public override string CommandName => "say_hello"; 13 | 14 | public SayHelloCommand(UIApplication uiApp) 15 | : base(new SayHelloEventHandler(), uiApp) 16 | { 17 | } 18 | 19 | public override object Execute(JObject parameters, string requestId) 20 | { 21 | RaiseAndWaitForCompletion(15000); 22 | return CommandResult.CreateSuccess(new { execute = true }); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /SampleCommandSet/Commands/Test/SayHelloEventHandler.cs: -------------------------------------------------------------------------------- 1 | using Autodesk.Revit.UI; 2 | using revit_mcp_sdk.API.Interfaces; 3 | using System.Threading; 4 | 5 | namespace SampleCommandSet.Commands.Test 6 | { 7 | public class SayHelloEventHandler : IExternalEventHandler, IWaitableExternalEventHandler 8 | { 9 | private readonly ManualResetEvent _resetEvent = new ManualResetEvent(false); 10 | 11 | public bool WaitForCompletion(int timeoutMilliseconds = 10000) 12 | { 13 | return _resetEvent.WaitOne(timeoutMilliseconds); 14 | } 15 | 16 | public void Execute(UIApplication app) 17 | { 18 | TaskDialog.Show("revit-mcp", "hello MCP"); 19 | } 20 | 21 | public string GetName() 22 | { 23 | return "say hello"; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /SampleCommandSet/Extensions/RevitApiCompatibilityExtensions.cs: -------------------------------------------------------------------------------- 1 | using Autodesk.Revit.DB; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace SampleCommandSet.Extensions 10 | { 11 | /// 12 | /// 提供 Revit API 跨版本兼容性的扩展方法 13 | /// 14 | public static class RevitApiCompatibilityExtensions 15 | { 16 | // 缓存反射结果以提高性能 17 | private static readonly Lazy ElementIdValueProperty = 18 | new Lazy(() => typeof(ElementId).GetProperty("Value")); 19 | 20 | private static readonly Lazy ElementIdIntegerValueProperty = 21 | new Lazy(() => typeof(ElementId).GetProperty("IntegerValue")); 22 | 23 | /// 24 | /// 以版本兼容的方式获取 ElementId 的整数值 25 | /// 26 | public static int GetIdValue(this ElementId id) 27 | { 28 | if (id == null) 29 | throw new ArgumentNullException(nameof(id)); 30 | 31 | // 先检查是否有 Value 属性 (Revit 2022+) 32 | if (ElementIdValueProperty.Value != null) 33 | { 34 | try 35 | { 36 | return (int)ElementIdValueProperty.Value.GetValue(id); 37 | } 38 | catch 39 | { 40 | // 失败时回退到 IntegerValue 41 | } 42 | } 43 | 44 | // 使用 IntegerValue (旧版本 Revit) 45 | return id.IntegerValue; 46 | } 47 | 48 | /// 49 | /// 获取文档当前 Revit 版本号 50 | /// 51 | public static int GetRevitVersionNumber(this Document doc) 52 | { 53 | if (doc == null) 54 | throw new ArgumentNullException(nameof(doc)); 55 | 56 | string versionString = doc.Application.VersionNumber; 57 | 58 | if (int.TryParse(versionString, out int versionNumber)) 59 | { 60 | return versionNumber; 61 | } 62 | return 0; 63 | } 64 | 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /SampleCommandSet/Models/FamilyTypeInfo.cs: -------------------------------------------------------------------------------- 1 | namespace SampleCommandSet.Models 2 | { 3 | public class FamilyTypeInfo 4 | { 5 | public long FamilyTypeId { get; set; } 6 | public string UniqueId { get; set; } 7 | public string FamilyName { get; set; } 8 | public string TypeName { get; set; } 9 | public string Category { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /SampleCommandSet/Models/Point.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace SampleCommandSet.Models 4 | { 5 | /// 6 | /// 点坐标 7 | /// 8 | public class Point 9 | { 10 | [JsonProperty("x")] 11 | public double X { get; set; } 12 | [JsonProperty("y")] 13 | public double Y { get; set; } 14 | [JsonProperty("z")] 15 | public double Z { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /SampleCommandSet/Models/ViewInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SampleCommandSet.Models 8 | { 9 | /// 10 | /// 视图信息数据结构 11 | /// 12 | public class ViewInfo 13 | { 14 | public long Id { get; set; } 15 | public string UniqueId { get; set; } 16 | public string Name { get; set; } 17 | public string ViewType { get; set; } 18 | public bool IsTemplate { get; set; } 19 | public int Scale { get; set; } 20 | public string DetailLevel { get; set; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /SampleCommandSet/Models/WallInfo.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace SampleCommandSet.Models 4 | { 5 | /// 6 | /// 墙体信息结构,用于返回创建的墙的详细信息 7 | /// 8 | public class WallInfo 9 | { 10 | [JsonProperty("elementId")] 11 | public int ElementId { get; set; } 12 | [JsonProperty("startPoint")] 13 | public Point StartPoint { get; set; } = new Point(); 14 | [JsonProperty("endPoint")] 15 | public Point EndPoint { get; set; } = new Point(); 16 | [JsonProperty("height")] 17 | public double Height { get; set; } 18 | [JsonProperty("thickness")] 19 | public double Thickness { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SampleCommandSet/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("SampleCommandSet")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("P R C")] 12 | [assembly: AssemblyProduct("SampleCommandSet")] 13 | [assembly: AssemblyCopyright("Copyright © P R C 2025")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 会使此程序集中的类型 18 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("1080b354-f092-49b9-a708-53e971d6ea95")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 33 | //通过使用 "*",如下所示: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /SampleCommandSet/SampleCommandSet.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {1080B354-F092-49B9-A708-53E971D6EA95} 8 | Library 9 | Properties 10 | SampleCommandSet 11 | SampleCommandSet 12 | v4.8 13 | 512 14 | true 15 | 16 | 17 | true 18 | ..\revit-mcp-plugin\bin\x64\Debug\commands\SampleCommandset\2019\ 19 | DEBUG;TRACE 20 | full 21 | x64 22 | 7.3 23 | prompt 24 | 25 | 26 | ..\revit-mcp-plugin\bin\x64\Release\commands\SampleCommandset\2019\ 27 | TRACE 28 | true 29 | pdbonly 30 | x64 31 | 7.3 32 | prompt 33 | 34 | 35 | true 36 | ..\revit-mcp-plugin\bin\x64\Debug\commands\SampleCommandset\2020\ 37 | DEBUG;TRACE 38 | full 39 | x64 40 | 7.3 41 | prompt 42 | 43 | 44 | ..\revit-mcp-plugin\bin\x64\Release\commands\SampleCommandset\2020\ 45 | TRACE 46 | true 47 | pdbonly 48 | x64 49 | 7.3 50 | prompt 51 | 52 | 53 | true 54 | ..\revit-mcp-plugin\bin\x64\Debug\commands\SampleCommandset\2021\ 55 | DEBUG;TRACE 56 | full 57 | x64 58 | 7.3 59 | prompt 60 | 61 | 62 | ..\revit-mcp-plugin\bin\x64\Release\commands\SampleCommandset\2021\ 63 | TRACE 64 | true 65 | pdbonly 66 | x64 67 | 7.3 68 | prompt 69 | 70 | 71 | true 72 | ..\revit-mcp-plugin\bin\x64\Debug\commands\SampleCommandset\2022\ 73 | DEBUG;TRACE 74 | full 75 | x64 76 | 7.3 77 | prompt 78 | 79 | 80 | ..\revit-mcp-plugin\bin\x64\Release\commands\SampleCommandset\2022\ 81 | TRACE 82 | true 83 | pdbonly 84 | x64 85 | 7.3 86 | prompt 87 | 88 | 89 | true 90 | ..\revit-mcp-plugin\bin\x64\Debug\commands\SampleCommandset\2023\ 91 | DEBUG;TRACE 92 | full 93 | x64 94 | 7.3 95 | prompt 96 | 97 | 98 | ..\revit-mcp-plugin\bin\x64\Release\commands\SampleCommandset\2023\ 99 | TRACE 100 | true 101 | pdbonly 102 | x64 103 | 7.3 104 | prompt 105 | 106 | 107 | ..\revit-mcp-plugin\bin\x64\Debug\commands\SampleCommandset\2024\ 108 | TRACE 109 | true 110 | pdbonly 111 | ARM64 112 | 7.3 113 | prompt 114 | 115 | 116 | ..\revit-mcp-plugin\bin\x64\Release\commands\SampleCommandset\2024\ 117 | TRACE 118 | true 119 | pdbonly 120 | x64 121 | 7.3 122 | prompt 123 | 124 | 125 | 126 | ..\packages\Revit_API_x64.2024.0.2\lib\NET480\AdWindows.dll 127 | False 128 | 129 | 130 | ..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll 131 | 132 | 133 | ..\packages\revit-mcp-sdk.1.0.0-beta.1\lib\net48\revit-mcp-sdk.dll 134 | 135 | 136 | ..\packages\Revit_API_x64.2024.0.2\lib\NET480\RevitAddInUtility.dll 137 | False 138 | 139 | 140 | ..\packages\Revit_API_x64.2024.0.2\lib\NET480\RevitAPI.dll 141 | False 142 | 143 | 144 | ..\packages\Revit_API_x64.2024.0.2\lib\NET480\RevitAPIUI.dll 145 | False 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | ..\packages\Revit_API_x64.2024.0.2\lib\NET480\UIFramework.dll 157 | False 158 | 159 | 160 | ..\packages\Revit_API_x64.2024.0.2\lib\NET480\UIFrameworkServices.dll 161 | False 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | -------------------------------------------------------------------------------- /SampleCommandSet/command.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SampleCommandSet", 3 | "description": "Basic command collection for Revit AI assistance", 4 | "developer": { 5 | "name": "revit-mcp", 6 | "email": "", 7 | "website": "", 8 | "organization": "revit-mcp" 9 | }, 10 | "commands": [ 11 | { 12 | "commandName": "say_hello", 13 | "description": "Displays a greeting dialog", 14 | "assemblyPath": "SampleCommandSet.dll" 15 | }, 16 | { 17 | "commandName": "get_available_family_types", 18 | "description": "get available family types", 19 | "assemblyPath": "SampleCommandSet.dll" 20 | }, 21 | { 22 | "commandName": "get_current_view_elements", 23 | "description": "get current view elements", 24 | "assemblyPath": "SampleCommandSet.dll" 25 | }, 26 | { 27 | "commandName": "get_current_view_info", 28 | "description": "get current view info", 29 | "assemblyPath": "SampleCommandSet.dll" 30 | }, 31 | { 32 | "commandName": "create_Wall", 33 | "description": "create a wall", 34 | "assemblyPath": "SampleCommandSet.dll" 35 | }, 36 | { 37 | "commandName": "delete_element", 38 | "description": "Deletes elements using ElementId", 39 | "assemblyPath": "SampleCommandSet.dll" 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /SampleCommandSet/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /revit-mcp-plugin.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.9.34714.143 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "revit-mcp-plugin", "revit-mcp-plugin\revit-mcp-plugin.csproj", "{43CD0FD7-DF41-4F64-92BE-A0F78666D86F}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug R20|Any CPU = Debug R20|Any CPU 11 | Debug R21|Any CPU = Debug R21|Any CPU 12 | Debug R22|Any CPU = Debug R22|Any CPU 13 | Debug R23|Any CPU = Debug R23|Any CPU 14 | Debug R24|Any CPU = Debug R24|Any CPU 15 | Debug R25|Any CPU = Debug R25|Any CPU 16 | Release R20|Any CPU = Release R20|Any CPU 17 | Release R21|Any CPU = Release R21|Any CPU 18 | Release R22|Any CPU = Release R22|Any CPU 19 | Release R23|Any CPU = Release R23|Any CPU 20 | Release R24|Any CPU = Release R24|Any CPU 21 | Release R25|Any CPU = Release R25|Any CPU 22 | EndGlobalSection 23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 24 | {43CD0FD7-DF41-4F64-92BE-A0F78666D86F}.Debug R20|Any CPU.ActiveCfg = Debug R20|Any CPU 25 | {43CD0FD7-DF41-4F64-92BE-A0F78666D86F}.Debug R20|Any CPU.Build.0 = Debug R20|Any CPU 26 | {43CD0FD7-DF41-4F64-92BE-A0F78666D86F}.Debug R21|Any CPU.ActiveCfg = Debug R21|Any CPU 27 | {43CD0FD7-DF41-4F64-92BE-A0F78666D86F}.Debug R21|Any CPU.Build.0 = Debug R21|Any CPU 28 | {43CD0FD7-DF41-4F64-92BE-A0F78666D86F}.Debug R22|Any CPU.ActiveCfg = Debug R22|Any CPU 29 | {43CD0FD7-DF41-4F64-92BE-A0F78666D86F}.Debug R22|Any CPU.Build.0 = Debug R22|Any CPU 30 | {43CD0FD7-DF41-4F64-92BE-A0F78666D86F}.Debug R23|Any CPU.ActiveCfg = Debug R23|Any CPU 31 | {43CD0FD7-DF41-4F64-92BE-A0F78666D86F}.Debug R23|Any CPU.Build.0 = Debug R23|Any CPU 32 | {43CD0FD7-DF41-4F64-92BE-A0F78666D86F}.Debug R24|Any CPU.ActiveCfg = Debug R24|Any CPU 33 | {43CD0FD7-DF41-4F64-92BE-A0F78666D86F}.Debug R24|Any CPU.Build.0 = Debug R24|Any CPU 34 | {43CD0FD7-DF41-4F64-92BE-A0F78666D86F}.Debug R25|Any CPU.ActiveCfg = Debug R25|Any CPU 35 | {43CD0FD7-DF41-4F64-92BE-A0F78666D86F}.Debug R25|Any CPU.Build.0 = Debug R25|Any CPU 36 | {43CD0FD7-DF41-4F64-92BE-A0F78666D86F}.Release R20|Any CPU.ActiveCfg = Release R20|Any CPU 37 | {43CD0FD7-DF41-4F64-92BE-A0F78666D86F}.Release R20|Any CPU.Build.0 = Release R20|Any CPU 38 | {43CD0FD7-DF41-4F64-92BE-A0F78666D86F}.Release R21|Any CPU.ActiveCfg = Release R21|Any CPU 39 | {43CD0FD7-DF41-4F64-92BE-A0F78666D86F}.Release R21|Any CPU.Build.0 = Release R21|Any CPU 40 | {43CD0FD7-DF41-4F64-92BE-A0F78666D86F}.Release R22|Any CPU.ActiveCfg = Release R22|Any CPU 41 | {43CD0FD7-DF41-4F64-92BE-A0F78666D86F}.Release R22|Any CPU.Build.0 = Release R22|Any CPU 42 | {43CD0FD7-DF41-4F64-92BE-A0F78666D86F}.Release R23|Any CPU.ActiveCfg = Release R23|Any CPU 43 | {43CD0FD7-DF41-4F64-92BE-A0F78666D86F}.Release R23|Any CPU.Build.0 = Release R23|Any CPU 44 | {43CD0FD7-DF41-4F64-92BE-A0F78666D86F}.Release R24|Any CPU.ActiveCfg = Release R24|Any CPU 45 | {43CD0FD7-DF41-4F64-92BE-A0F78666D86F}.Release R24|Any CPU.Build.0 = Release R24|Any CPU 46 | {43CD0FD7-DF41-4F64-92BE-A0F78666D86F}.Release R25|Any CPU.ActiveCfg = Release R25|Any CPU 47 | {43CD0FD7-DF41-4F64-92BE-A0F78666D86F}.Release R25|Any CPU.Build.0 = Release R25|Any CPU 48 | EndGlobalSection 49 | GlobalSection(SolutionProperties) = preSolution 50 | HideSolutionNode = FALSE 51 | EndGlobalSection 52 | GlobalSection(ExtensibilityGlobals) = postSolution 53 | SolutionGuid = {2A512456-F260-4FAB-BAC5-E4BCEC54F581} 54 | EndGlobalSection 55 | EndGlobal 56 | -------------------------------------------------------------------------------- /revit-mcp-plugin/Configuration/CommandConfig.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace revit_mcp_plugin.Configuration 9 | { 10 | /// 11 | /// 命令配置类 12 | /// 13 | public class CommandConfig 14 | { 15 | /// 16 | /// 命令名称 - 对应IRevitCommand.CommandName 17 | /// 18 | [JsonProperty("commandName")] 19 | public string CommandName { get; set; } 20 | 21 | /// 22 | /// 程序集路径 - 包含此命令的DLL 23 | /// 24 | [JsonProperty("assemblyPath")] 25 | public string AssemblyPath { get; set; } 26 | 27 | /// 28 | /// 是否启用该命令 29 | /// 30 | [JsonProperty("enabled")] 31 | public bool Enabled { get; set; } = true; 32 | 33 | /// 34 | /// 支持的Revit版本 35 | /// 36 | [JsonProperty("supportedRevitVersions")] 37 | public string[] SupportedRevitVersions { get; set; } = new string[0]; 38 | 39 | /// 40 | /// 开发者信息 41 | /// 42 | [JsonProperty("developer")] 43 | public DeveloperInfo Developer { get; set; } = new DeveloperInfo(); 44 | 45 | /// 46 | /// 命令描述 47 | /// 48 | [JsonProperty("description")] 49 | public string Description { get; set; } = ""; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /revit-mcp-plugin/Configuration/ConfigurationManager.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using RevitMCPSDK.API.Interfaces; 3 | using revit_mcp_plugin.Utils; 4 | using System; 5 | using System.IO; 6 | 7 | namespace revit_mcp_plugin.Configuration 8 | { 9 | public class ConfigurationManager 10 | { 11 | private readonly ILogger _logger; 12 | private readonly string _configPath; 13 | 14 | public FrameworkConfig Config { get; private set; } 15 | 16 | public ConfigurationManager(ILogger logger) 17 | { 18 | _logger = logger; 19 | 20 | // 配置文件路径 21 | _configPath = PathManager.GetCommandRegistryFilePath(); 22 | } 23 | 24 | /// 25 | /// 加载配置 26 | /// 27 | public void LoadConfiguration() 28 | { 29 | try 30 | { 31 | if (File.Exists(_configPath)) 32 | { 33 | string json = File.ReadAllText(_configPath); 34 | Config = JsonConvert.DeserializeObject(json); 35 | _logger.Info("已加载配置文件: {0}", _configPath); 36 | } 37 | else 38 | { 39 | _logger.Error("未找到配置文件"); 40 | } 41 | } 42 | catch (Exception ex) 43 | { 44 | _logger.Error("加载配置文件失败: {0}", ex.Message); 45 | } 46 | 47 | // 记录加载时间 48 | _lastConfigLoadTime = DateTime.Now; 49 | } 50 | 51 | ///// 52 | ///// 重新加载配置 53 | ///// 54 | //public void RefreshConfiguration() 55 | //{ 56 | // LoadConfiguration(); 57 | // _logger.Info("配置已重新加载"); 58 | //} 59 | 60 | //public bool HasConfigChanged() 61 | //{ 62 | // if (!File.Exists(_configPath)) 63 | // return false; 64 | 65 | // DateTime lastWrite = File.GetLastWriteTime(_configPath); 66 | // return lastWrite > _lastConfigLoadTime; 67 | //} 68 | 69 | private DateTime _lastConfigLoadTime; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /revit-mcp-plugin/Configuration/DeveloperInfo.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace revit_mcp_plugin.Configuration 9 | { 10 | /// 11 | /// 开发者信息 12 | /// 13 | public class DeveloperInfo 14 | { 15 | /// 16 | /// 开发者名称 17 | /// 18 | [JsonProperty("name")] 19 | public string Name { get; set; } = ""; 20 | 21 | /// 22 | /// 开发者邮箱 23 | /// 24 | [JsonProperty("email")] 25 | public string Email { get; set; } = ""; 26 | 27 | /// 28 | /// 开发者网站 29 | /// 30 | [JsonProperty("website")] 31 | public string Website { get; set; } = ""; 32 | 33 | /// 34 | /// 开发者组织 35 | /// 36 | [JsonProperty("organization")] 37 | public string Organization { get; set; } = ""; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /revit-mcp-plugin/Configuration/FrameworkConfig.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace revit_mcp_plugin.Configuration 9 | { 10 | /// 11 | /// 框架配置类 12 | /// 13 | public class FrameworkConfig 14 | { 15 | /// 16 | /// 命令配置列表 17 | /// 18 | [JsonProperty("commands")] 19 | public List Commands { get; set; } = new List(); 20 | 21 | /// 22 | /// 全局设置 23 | /// 24 | [JsonProperty("settings")] 25 | public ServiceSettings Settings { get; set; } = new ServiceSettings(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /revit-mcp-plugin/Configuration/ServiceSettings.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace revit_mcp_plugin.Configuration 9 | { 10 | /// 11 | /// 服务设置类 12 | /// 13 | public class ServiceSettings 14 | { 15 | /// 16 | /// 日志级别 17 | /// 18 | [JsonProperty("logLevel")] 19 | public string LogLevel { get; set; } = "Info"; 20 | 21 | /// 22 | /// socket服务端口 23 | /// 24 | [JsonProperty("port")] 25 | public int Port { get; set; } = 8080; 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /revit-mcp-plugin/Core/Application.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Autodesk.Revit.UI; 3 | using System.Reflection; 4 | using System.Windows.Media.Imaging; 5 | 6 | 7 | 8 | namespace revit_mcp_plugin.Core 9 | { 10 | public class Application : IExternalApplication 11 | { 12 | public Result OnStartup(UIControlledApplication application) 13 | { 14 | RibbonPanel mcpPanel = application.CreateRibbonPanel("Revit MCP Plugin"); 15 | 16 | PushButtonData pushButtonData = new PushButtonData("ID_EXCMD_TOGGLE_REVIT_MCP", "Revit MCP\r\n Switch", 17 | Assembly.GetExecutingAssembly().Location, "revit_mcp_plugin.Core.MCPServiceConnection"); 18 | pushButtonData.ToolTip = "Open / Close mcp server"; 19 | pushButtonData.Image = new BitmapImage(new Uri("/revit-mcp-plugin;component/Core/Ressources/icon-16.png", UriKind.RelativeOrAbsolute)); 20 | pushButtonData.LargeImage = new BitmapImage(new Uri("/revit-mcp-plugin;component/Core/Ressources/icon-32.png", UriKind.RelativeOrAbsolute)); 21 | mcpPanel.AddItem(pushButtonData); 22 | 23 | PushButtonData mcp_settings_pushButtonData = new PushButtonData("ID_EXCMD_MCP_SETTINGS", "Settings", 24 | Assembly.GetExecutingAssembly().Location, "revit_mcp_plugin.Core.Settings"); 25 | mcp_settings_pushButtonData.ToolTip = "MCP Settings"; 26 | mcp_settings_pushButtonData.Image = new BitmapImage(new Uri("/revit-mcp-plugin;component/Core/Ressources/settings-16.png", UriKind.RelativeOrAbsolute)); 27 | mcp_settings_pushButtonData.LargeImage = new BitmapImage(new Uri("/revit-mcp-plugin;component/Core/Ressources/settings-32.png", UriKind.RelativeOrAbsolute)); 28 | mcpPanel.AddItem(mcp_settings_pushButtonData); 29 | 30 | return Result.Succeeded; 31 | } 32 | 33 | public Result OnShutdown(UIControlledApplication application) 34 | { 35 | try 36 | { 37 | if (SocketService.Instance.IsRunning) 38 | { 39 | SocketService.Instance.Stop(); 40 | } 41 | } 42 | catch { } 43 | 44 | return Result.Succeeded; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /revit-mcp-plugin/Core/CommandExecutor.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using RevitMCPSDK.API.Interfaces; 3 | using RevitMCPSDK.API.Models; 4 | using RevitMCPSDK.API.Models.JsonRPC; 5 | using RevitMCPSDK.Exceptions; 6 | using System; 7 | 8 | namespace revit_mcp_plugin.Core 9 | { 10 | public class CommandExecutor 11 | { 12 | private readonly ICommandRegistry _commandRegistry; 13 | private readonly ILogger _logger; 14 | 15 | public CommandExecutor(ICommandRegistry commandRegistry, ILogger logger) 16 | { 17 | _commandRegistry = commandRegistry; 18 | _logger = logger; 19 | } 20 | 21 | public string ExecuteCommand(JsonRPCRequest request) 22 | { 23 | try 24 | { 25 | // 查找命令 26 | if (!_commandRegistry.TryGetCommand(request.Method, out var command)) 27 | { 28 | _logger.Warning("未找到命令: {0}", request.Method); 29 | return CreateErrorResponse(request.Id, 30 | JsonRPCErrorCodes.MethodNotFound, 31 | $"未找到方法: '{request.Method}'"); 32 | } 33 | 34 | _logger.Info("执行命令: {0}", request.Method); 35 | 36 | // 执行命令 37 | try 38 | { 39 | object result = command.Execute(request.GetParamsObject(), request.Id); 40 | _logger.Info("命令 {0} 执行成功", request.Method); 41 | 42 | return CreateSuccessResponse(request.Id, result); 43 | } 44 | catch (CommandExecutionException ex) 45 | { 46 | _logger.Error("命令 {0} 执行失败: {1}", request.Method, ex.Message); 47 | return CreateErrorResponse(request.Id, 48 | ex.ErrorCode, 49 | ex.Message, 50 | ex.ErrorData); 51 | } 52 | catch (Exception ex) 53 | { 54 | _logger.Error("命令 {0} 执行时发生异常: {1}", request.Method, ex.Message); 55 | return CreateErrorResponse(request.Id, 56 | JsonRPCErrorCodes.InternalError, 57 | ex.Message); 58 | } 59 | } 60 | catch (Exception ex) 61 | { 62 | _logger.Error("执行命令处理过程中发生异常: {0}", ex.Message); 63 | return CreateErrorResponse(request.Id, 64 | JsonRPCErrorCodes.InternalError, 65 | $"内部错误: {ex.Message}"); 66 | } 67 | } 68 | 69 | private string CreateSuccessResponse(string id, object result) 70 | { 71 | var response = new JsonRPCSuccessResponse 72 | { 73 | Id = id, 74 | Result = result is JToken jToken ? jToken : JToken.FromObject(result) 75 | }; 76 | 77 | return response.ToJson(); 78 | } 79 | 80 | private string CreateErrorResponse(string id, int code, string message, object data = null) 81 | { 82 | var response = new JsonRPCErrorResponse 83 | { 84 | Id = id, 85 | Error = new JsonRPCError 86 | { 87 | Code = code, 88 | Message = message, 89 | Data = data != null ? JToken.FromObject(data) : null 90 | } 91 | }; 92 | 93 | return response.ToJson(); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /revit-mcp-plugin/Core/CommandManager.cs: -------------------------------------------------------------------------------- 1 | using Autodesk.Revit.UI; 2 | using RevitMCPSDK.API.Interfaces; 3 | using RevitMCPSDK.API.Utils; 4 | using revit_mcp_plugin.Configuration; 5 | using revit_mcp_plugin.Utils; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.IO; 9 | using System.Linq; 10 | using System.Reflection; 11 | using System.Text; 12 | using System.Threading.Tasks; 13 | 14 | namespace revit_mcp_plugin.Core 15 | { 16 | /// 17 | /// 命令管理器,负责加载和管理命令 18 | /// 19 | public class CommandManager 20 | { 21 | private readonly ICommandRegistry _commandRegistry; 22 | private readonly ILogger _logger; 23 | private readonly ConfigurationManager _configManager; 24 | private readonly UIApplication _uiApplication; 25 | private readonly RevitVersionAdapter _versionAdapter; 26 | 27 | public CommandManager( 28 | ICommandRegistry commandRegistry, 29 | ILogger logger, 30 | ConfigurationManager configManager, 31 | UIApplication uiApplication) 32 | { 33 | _commandRegistry = commandRegistry; 34 | _logger = logger; 35 | _configManager = configManager; 36 | _uiApplication = uiApplication; 37 | _versionAdapter = new RevitVersionAdapter(_uiApplication.Application); 38 | } 39 | 40 | /// 41 | /// 加载配置文件中指定的所有命令 42 | /// 43 | public void LoadCommands() 44 | { 45 | _logger.Info("开始加载命令"); 46 | string currentVersion = _versionAdapter.GetRevitVersion(); 47 | _logger.Info("当前 Revit 版本: {0}", currentVersion); 48 | 49 | // 从配置加载外部命令 50 | foreach (var commandConfig in _configManager.Config.Commands) 51 | { 52 | try 53 | { 54 | if (!commandConfig.Enabled) 55 | { 56 | _logger.Info("跳过禁用的命令: {0}", commandConfig.CommandName); 57 | continue; 58 | } 59 | 60 | // 检查版本兼容性 61 | if (commandConfig.SupportedRevitVersions != null && 62 | commandConfig.SupportedRevitVersions.Length > 0 && 63 | !_versionAdapter.IsVersionSupported(commandConfig.SupportedRevitVersions)) 64 | { 65 | _logger.Warning("命令 {0} 不支持当前 Revit 版本 {1},已跳过", 66 | commandConfig.CommandName, currentVersion); 67 | continue; 68 | } 69 | 70 | // 替换路径中的版本占位符 71 | commandConfig.AssemblyPath = commandConfig.AssemblyPath.Contains("{VERSION}") 72 | ? commandConfig.AssemblyPath.Replace("{VERSION}", currentVersion) 73 | : commandConfig.AssemblyPath; 74 | 75 | // 加载外部命令程序集 76 | LoadCommandFromAssembly(commandConfig); 77 | } 78 | catch (Exception ex) 79 | { 80 | _logger.Error("加载命令 {0} 失败: {1}", commandConfig.CommandName, ex.Message); 81 | } 82 | } 83 | 84 | _logger.Info("命令加载完成"); 85 | } 86 | 87 | /// 88 | /// 加载特定程序集中的特定命令 89 | /// 90 | /// 命令名称 91 | /// 程序集路径 92 | private void LoadCommandFromAssembly(CommandConfig config) 93 | { 94 | try 95 | { 96 | // 确定程序集路径 97 | string assemblyPath = config.AssemblyPath; 98 | if (!Path.IsPathRooted(assemblyPath)) 99 | { 100 | // 如果不是绝对路径,则相对于Commands目录 101 | string baseDir = PathManager.GetCommandsDirectoryPath(); 102 | assemblyPath = Path.Combine(baseDir, assemblyPath); 103 | } 104 | 105 | if (!File.Exists(assemblyPath)) 106 | { 107 | _logger.Error("命令程序集不存在: {0}", assemblyPath); 108 | return; 109 | } 110 | 111 | // 加载程序集 112 | Assembly assembly = Assembly.LoadFrom(assemblyPath); 113 | 114 | // 查找实现 IRevitCommand 接口的类型 115 | foreach (Type type in assembly.GetTypes()) 116 | { 117 | if (typeof(RevitMCPSDK.API.Interfaces.IRevitCommand).IsAssignableFrom(type) && 118 | !type.IsInterface && 119 | !type.IsAbstract) 120 | { 121 | try 122 | { 123 | // 创建命令实例 124 | RevitMCPSDK.API.Interfaces.IRevitCommand command; 125 | 126 | // 检查命令是否实现了可初始化接口 127 | if (typeof(IRevitCommandInitializable).IsAssignableFrom(type)) 128 | { 129 | // 创建实例并初始化 130 | command = (IRevitCommand)Activator.CreateInstance(type); 131 | ((IRevitCommandInitializable)command).Initialize(_uiApplication); 132 | } 133 | else 134 | { 135 | // 尝试查找接受 UIApplication 的构造函数 136 | var constructor = type.GetConstructor(new[] { typeof(UIApplication) }); 137 | if (constructor != null) 138 | { 139 | command = (IRevitCommand)constructor.Invoke(new object[] { _uiApplication }); 140 | } 141 | else 142 | { 143 | // 使用无参构造函数 144 | command = (IRevitCommand)Activator.CreateInstance(type); 145 | } 146 | } 147 | 148 | // 检查命令名称是否与配置匹配 149 | if (command.CommandName == config.CommandName) 150 | { 151 | _commandRegistry.RegisterCommand(command); 152 | _logger.Info("已注册外部命令: {0} (来自 {1})", 153 | command.CommandName, Path.GetFileName(assemblyPath)); 154 | break; // 找到匹配的命令后退出循环 155 | } 156 | } 157 | catch (Exception ex) 158 | { 159 | _logger.Error("创建命令实例失败 [{0}]: {1}", type.FullName, ex.Message); 160 | } 161 | } 162 | } 163 | } 164 | catch (Exception ex) 165 | { 166 | _logger.Error("加载命令程序集失败: {0}", ex.Message); 167 | } 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /revit-mcp-plugin/Core/ExternalEventManager.cs: -------------------------------------------------------------------------------- 1 | using Autodesk.Revit.UI; 2 | using Autodesk.Revit.DB; 3 | using RevitMCPSDK.API.Interfaces; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace revit_mcp_plugin.Core 11 | { 12 | /// 13 | /// 管理外部事件的创建和生命周期 14 | /// 15 | public class ExternalEventManager 16 | { 17 | private static ExternalEventManager _instance; 18 | private Dictionary _events = new Dictionary(); 19 | private bool _isInitialized = false; 20 | private UIApplication _uiApp; 21 | private ILogger _logger; 22 | 23 | public static ExternalEventManager Instance 24 | { 25 | get 26 | { 27 | if (_instance == null) 28 | _instance = new ExternalEventManager(); 29 | return _instance; 30 | } 31 | } 32 | 33 | private ExternalEventManager() { } 34 | 35 | public void Initialize(UIApplication uiApp, ILogger logger) 36 | { 37 | _uiApp = uiApp; 38 | _logger = logger; 39 | _isInitialized = true; 40 | } 41 | 42 | /// 43 | /// 获取或创建外部事件 44 | /// 45 | public ExternalEvent GetOrCreateEvent(IWaitableExternalEventHandler handler, string key) 46 | { 47 | if (!_isInitialized) 48 | throw new InvalidOperationException("ExternalEventManager 尚未初始化"); 49 | 50 | // 如果存在且处理器匹配,直接返回 51 | if (_events.TryGetValue(key, out var wrapper) && 52 | wrapper.Handler == handler) 53 | { 54 | return wrapper.Event; 55 | } 56 | 57 | // 需要在UI线程中创建事件 58 | ExternalEvent externalEvent = null; 59 | 60 | // 使用活动文档的上下文执行创建事件的操作 61 | _uiApp.ActiveUIDocument.Document.Application.ExecuteCommand( 62 | (uiApp) => { 63 | externalEvent = ExternalEvent.Create(handler); 64 | } 65 | ); 66 | 67 | if (externalEvent == null) 68 | throw new InvalidOperationException("无法创建外部事件"); 69 | 70 | // 存储事件 71 | _events[key] = new ExternalEventWrapper 72 | { 73 | Event = externalEvent, 74 | Handler = handler 75 | }; 76 | 77 | _logger.Info($"为 {key} 创建了新的外部事件"); 78 | 79 | return externalEvent; 80 | } 81 | 82 | /// 83 | /// 清除事件缓存 84 | /// 85 | public void ClearEvents() 86 | { 87 | _events.Clear(); 88 | } 89 | 90 | private class ExternalEventWrapper 91 | { 92 | public ExternalEvent Event { get; set; } 93 | public IWaitableExternalEventHandler Handler { get; set; } 94 | } 95 | } 96 | } 97 | 98 | namespace Autodesk.Revit.DB 99 | { 100 | public static class ApplicationExtensions 101 | { 102 | public delegate void CommandDelegate(UIApplication uiApp); 103 | 104 | /// 105 | /// 在 Revit 上下文中执行命令 106 | /// 107 | public static void ExecuteCommand(this Autodesk.Revit.ApplicationServices.Application app, CommandDelegate command) 108 | { 109 | // 这个方法在 Revit 上下文中调用,可以安全地创建 ExternalEvent 110 | command?.Invoke(new UIApplication(app)); 111 | } 112 | } 113 | } -------------------------------------------------------------------------------- /revit-mcp-plugin/Core/MCPServiceConnection.cs: -------------------------------------------------------------------------------- 1 | using Autodesk.Revit.Attributes; 2 | using Autodesk.Revit.DB; 3 | using Autodesk.Revit.UI; 4 | using System; 5 | 6 | namespace revit_mcp_plugin.Core 7 | { 8 | [Transaction(TransactionMode.Manual)] 9 | public class MCPServiceConnection : IExternalCommand 10 | { 11 | public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) 12 | { 13 | try 14 | { 15 | // 获取socket服务 16 | SocketService service = SocketService.Instance; 17 | 18 | if (service.IsRunning) 19 | { 20 | service.Stop(); 21 | TaskDialog.Show("revitMCP", "Close Server"); 22 | } 23 | else 24 | { 25 | service.Initialize(commandData.Application); 26 | service.Start(); 27 | TaskDialog.Show("revitMCP", "Open Server"); 28 | } 29 | 30 | return Result.Succeeded; 31 | } 32 | catch (Exception ex) 33 | { 34 | message = ex.Message; 35 | return Result.Failed; 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /revit-mcp-plugin/Core/Ressources/icon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revit-mcp/revit-mcp-plugin/b11d645fad86f282f73b07394348e845fe7d7872/revit-mcp-plugin/Core/Ressources/icon-16.png -------------------------------------------------------------------------------- /revit-mcp-plugin/Core/Ressources/icon-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revit-mcp/revit-mcp-plugin/b11d645fad86f282f73b07394348e845fe7d7872/revit-mcp-plugin/Core/Ressources/icon-32.png -------------------------------------------------------------------------------- /revit-mcp-plugin/Core/Ressources/settings-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revit-mcp/revit-mcp-plugin/b11d645fad86f282f73b07394348e845fe7d7872/revit-mcp-plugin/Core/Ressources/settings-16.png -------------------------------------------------------------------------------- /revit-mcp-plugin/Core/Ressources/settings-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revit-mcp/revit-mcp-plugin/b11d645fad86f282f73b07394348e845fe7d7872/revit-mcp-plugin/Core/Ressources/settings-32.png -------------------------------------------------------------------------------- /revit-mcp-plugin/Core/RevitCommandRegistry.cs: -------------------------------------------------------------------------------- 1 | using RevitMCPSDK.API.Interfaces; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace revit_mcp_plugin.Core 9 | { 10 | public class RevitCommandRegistry : ICommandRegistry 11 | { 12 | private readonly Dictionary _commands = new Dictionary(); 13 | 14 | public void RegisterCommand(IRevitCommand command) 15 | { 16 | _commands[command.CommandName] = command; 17 | } 18 | 19 | public bool TryGetCommand(string commandName, out IRevitCommand command) 20 | { 21 | return _commands.TryGetValue(commandName, out command); 22 | } 23 | 24 | public void ClearCommands() 25 | { 26 | _commands.Clear(); 27 | } 28 | 29 | public IEnumerable GetRegisteredCommands() 30 | { 31 | return _commands.Keys; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /revit-mcp-plugin/Core/Settings.cs: -------------------------------------------------------------------------------- 1 | using Autodesk.Revit.Attributes; 2 | using Autodesk.Revit.DB; 3 | using Autodesk.Revit.UI; 4 | using revit_mcp_plugin.UI; 5 | 6 | namespace revit_mcp_plugin.Core 7 | { 8 | [Transaction(TransactionMode.Manual)] 9 | public class Settings : IExternalCommand 10 | { 11 | public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) 12 | { 13 | SettingsWindow window = new SettingsWindow(); 14 | _ = new System.Windows.Interop.WindowInteropHelper(window) 15 | { 16 | Owner = System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle 17 | }; 18 | window.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen; 19 | 20 | window.Show(); 21 | 22 | return Result.Succeeded; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /revit-mcp-plugin/Core/SocketService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net; 4 | using System.Net.Sockets; 5 | using System.Text; 6 | using System.Threading; 7 | using Autodesk.Revit.UI; 8 | using Newtonsoft.Json; 9 | using Newtonsoft.Json.Linq; 10 | using RevitMCPSDK.API.Models.JsonRPC; 11 | using RevitMCPSDK.API.Interfaces; 12 | using revit_mcp_plugin.Configuration; 13 | using revit_mcp_plugin.Utils; 14 | 15 | namespace revit_mcp_plugin.Core 16 | { 17 | public class SocketService 18 | { 19 | private static SocketService _instance; 20 | private TcpListener _listener; 21 | private Thread _listenerThread; 22 | private bool _isRunning; 23 | private int _port = 8080; 24 | private UIApplication _uiApp; 25 | private ICommandRegistry _commandRegistry; 26 | private ILogger _logger; 27 | private CommandExecutor _commandExecutor; 28 | 29 | public static SocketService Instance 30 | { 31 | get 32 | { 33 | if(_instance == null) 34 | _instance = new SocketService(); 35 | return _instance; 36 | } 37 | } 38 | 39 | private SocketService() 40 | { 41 | _commandRegistry = new RevitCommandRegistry(); 42 | _logger = new Logger(); 43 | } 44 | 45 | public bool IsRunning => _isRunning; 46 | 47 | public int Port 48 | { 49 | get => _port; 50 | set => _port = value; 51 | } 52 | 53 | // 初始化 54 | public void Initialize(UIApplication uiApp) 55 | { 56 | _uiApp = uiApp; 57 | 58 | // 初始化事件管理器 59 | ExternalEventManager.Instance.Initialize(uiApp, _logger); 60 | 61 | // 记录当前 Revit 版本 62 | var versionAdapter = new RevitMCPSDK.API.Utils.RevitVersionAdapter(_uiApp.Application); 63 | string currentVersion = versionAdapter.GetRevitVersion(); 64 | _logger.Info("当前 Revit 版本: {0}", currentVersion); 65 | 66 | 67 | 68 | // 创建命令执行器 69 | _commandExecutor = new CommandExecutor(_commandRegistry, _logger); 70 | 71 | // 加载配置并注册命令 72 | ConfigurationManager configManager = new ConfigurationManager(_logger); 73 | configManager.LoadConfiguration(); 74 | 75 | 76 | //// 从配置中读取服务端口 77 | //if (configManager.Config.Settings.Port > 0) 78 | //{ 79 | // _port = configManager.Config.Settings.Port; 80 | //} 81 | _port = 8080; // 固定端口号 82 | 83 | // 加载命令 84 | CommandManager commandManager = new CommandManager( 85 | _commandRegistry, _logger, configManager, _uiApp); 86 | commandManager.LoadCommands(); 87 | 88 | _logger.Info($"Socket service initialized on port {_port}"); 89 | } 90 | 91 | public void Start() 92 | { 93 | if (_isRunning) return; 94 | 95 | try 96 | { 97 | _isRunning = true; 98 | _listener = new TcpListener(IPAddress.Any, _port); 99 | _listener.Start(); 100 | 101 | _listenerThread = new Thread(ListenForClients) 102 | { 103 | IsBackground = true 104 | }; 105 | _listenerThread.Start(); 106 | } 107 | catch (Exception) 108 | { 109 | _isRunning = false; 110 | } 111 | } 112 | 113 | public void Stop() 114 | { 115 | if (!_isRunning) return; 116 | 117 | try 118 | { 119 | _isRunning = false; 120 | 121 | _listener?.Stop(); 122 | _listener = null; 123 | 124 | if(_listenerThread!=null && _listenerThread.IsAlive) 125 | { 126 | _listenerThread.Join(1000); 127 | } 128 | } 129 | catch (Exception) 130 | { 131 | // log error 132 | } 133 | } 134 | 135 | private void ListenForClients() 136 | { 137 | try 138 | { 139 | while (_isRunning) 140 | { 141 | TcpClient client = _listener.AcceptTcpClient(); 142 | 143 | Thread clientThread = new Thread(HandleClientCommunication) 144 | { 145 | IsBackground = true 146 | }; 147 | clientThread.Start(client); 148 | } 149 | } 150 | catch (SocketException) 151 | { 152 | 153 | } 154 | catch(Exception) 155 | { 156 | // log 157 | } 158 | } 159 | 160 | private void HandleClientCommunication(object clientObj) 161 | { 162 | TcpClient tcpClient = (TcpClient)clientObj; 163 | NetworkStream stream = tcpClient.GetStream(); 164 | 165 | try 166 | { 167 | byte[] buffer = new byte[8192]; 168 | 169 | while (_isRunning && tcpClient.Connected) 170 | { 171 | // 读取客户端消息 172 | int bytesRead = 0; 173 | 174 | try 175 | { 176 | bytesRead = stream.Read(buffer, 0, buffer.Length); 177 | } 178 | catch (IOException) 179 | { 180 | // 客户端断开连接 181 | break; 182 | } 183 | 184 | if (bytesRead == 0) 185 | { 186 | // 客户端断开连接 187 | break; 188 | } 189 | 190 | string message = Encoding.UTF8.GetString(buffer, 0, bytesRead); 191 | System.Diagnostics.Trace.WriteLine($"收到消息: {message}"); 192 | 193 | string response = ProcessJsonRPCRequest(message); 194 | 195 | // 发送响应 196 | byte[] responseData = Encoding.UTF8.GetBytes(response); 197 | stream.Write(responseData, 0, responseData.Length); 198 | } 199 | } 200 | catch(Exception) 201 | { 202 | // log 203 | } 204 | finally 205 | { 206 | tcpClient.Close(); 207 | } 208 | } 209 | 210 | private string ProcessJsonRPCRequest(string requestJson) 211 | { 212 | JsonRPCRequest request; 213 | 214 | try 215 | { 216 | // 解析JSON-RPC请求 217 | request = JsonConvert.DeserializeObject(requestJson); 218 | 219 | // 验证请求格式是否有效 220 | if (request == null || !request.IsValid()) 221 | { 222 | return CreateErrorResponse( 223 | null, 224 | JsonRPCErrorCodes.InvalidRequest, 225 | "Invalid JSON-RPC request" 226 | ); 227 | } 228 | 229 | // 查找命令 230 | if (!_commandRegistry.TryGetCommand(request.Method, out var command)) 231 | { 232 | return CreateErrorResponse(request.Id, JsonRPCErrorCodes.MethodNotFound, 233 | $"Method '{request.Method}' not found"); 234 | } 235 | 236 | // 执行命令 237 | try 238 | { 239 | object result = command.Execute(request.GetParamsObject(), request.Id); 240 | 241 | return CreateSuccessResponse(request.Id, result); 242 | } 243 | catch (Exception ex) 244 | { 245 | return CreateErrorResponse(request.Id, JsonRPCErrorCodes.InternalError, ex.Message); 246 | } 247 | } 248 | catch (JsonException) 249 | { 250 | // JSON解析错误 251 | return CreateErrorResponse( 252 | null, 253 | JsonRPCErrorCodes.ParseError, 254 | "Invalid JSON" 255 | ); 256 | } 257 | catch (Exception ex) 258 | { 259 | // 处理请求时的其他错误 260 | return CreateErrorResponse( 261 | null, 262 | JsonRPCErrorCodes.InternalError, 263 | $"Internal error: {ex.Message}" 264 | ); 265 | } 266 | } 267 | 268 | private string CreateSuccessResponse(string id, object result) 269 | { 270 | var response = new JsonRPCSuccessResponse 271 | { 272 | Id = id, 273 | Result = result is JToken jToken ? jToken : JToken.FromObject(result) 274 | }; 275 | 276 | return response.ToJson(); 277 | } 278 | 279 | private string CreateErrorResponse(string id, int code, string message, object data = null) 280 | { 281 | var response = new JsonRPCErrorResponse 282 | { 283 | Id = id, 284 | Error = new JsonRPCError 285 | { 286 | Code = code, 287 | Message = message, 288 | Data = data != null ? JToken.FromObject(data) : null 289 | } 290 | }; 291 | 292 | return response.ToJson(); 293 | } 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /revit-mcp-plugin/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("revit-mcp-plugin")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("P R C")] 12 | [assembly: AssemblyProduct("revit-mcp-plugin")] 13 | [assembly: AssemblyCopyright("Copyright © P R C 2025")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | 18 | // 将 ComVisible 设置为 false 会使此程序集中的类型 19 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 20 | //请将此类型的 ComVisible 特性设置为 true。 21 | [assembly: ComVisible(false)] 22 | 23 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 24 | [assembly: Guid("43cd0fd7-df41-4f64-92be-a0f78666d86f")] 25 | 26 | // 程序集的版本信息由下列四个值组成: 27 | // 28 | // 主版本 29 | // 次版本 30 | // 生成号 31 | // 修订号 32 | // 33 | //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 34 | //通过使用 "*",如下所示: 35 | // [assembly: AssemblyVersion("1.0.*")] 36 | [assembly: AssemblyVersion("1.0.0.0")] 37 | [assembly: AssemblyFileVersion("1.0.0.0")] 38 | 39 | #if NET5_0_OR_GREATER || NETCOREAPP3_1_OR_GREATER 40 | [assembly: System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] 41 | #endif -------------------------------------------------------------------------------- /revit-mcp-plugin/UI/CommandSetSettingsPage.xaml: -------------------------------------------------------------------------------- 1 |  9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 36 | 39 | 40 | 41 | 42 | 43 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 55 | 56 | 57 | 58 | 59 | 60 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 71 | 72 | 73 | 74 | 76 | 78 | 79 | 80 | 81 | 82 | 87 | 88 | 89 | 90 | 91 | 92 |