├── .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 |
93 |
94 |
95 |
96 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/revit-mcp-plugin/UI/CommandSetSettingsPage.xaml.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using revit_mcp_plugin.Configuration;
3 | using revit_mcp_plugin.Utils;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Collections.ObjectModel;
7 | using System.Diagnostics;
8 | using System.IO;
9 | using System.Linq;
10 | using System.Windows;
11 | using System.Windows.Controls;
12 | namespace revit_mcp_plugin.UI
13 | {
14 | ///
15 | /// Interaction logic for CommandSetSettingsPage.xaml
16 | ///
17 | public partial class CommandSetSettingsPage : Page
18 | {
19 | private ObservableCollection commandSets;
20 | private ObservableCollection currentCommands;
21 |
22 | public CommandSetSettingsPage()
23 | {
24 | InitializeComponent();
25 | // Initialize data collections
26 | commandSets = new ObservableCollection();
27 | currentCommands = new ObservableCollection();
28 | // Set data bindings
29 | CommandSetListBox.ItemsSource = commandSets;
30 | FeaturesListView.ItemsSource = currentCommands;
31 | // Load command sets
32 | LoadCommandSets();
33 | // Initial state
34 | NoSelectionTextBlock.Visibility = Visibility.Visible;
35 | }
36 |
37 | private void LoadCommandSets()
38 | {
39 | try
40 | {
41 | commandSets.Clear();
42 | string commandsDirectory = PathManager.GetCommandsDirectoryPath();
43 | string registryFilePath = PathManager.GetCommandRegistryFilePath();
44 | // 1. First load all command set folders, establish available command collections
45 | Dictionary availableCommandSets = new Dictionary();
46 | HashSet availableCommandNames = new HashSet();
47 | // Get all command set directories
48 | string[] commandSetDirectories = Directory.GetDirectories(commandsDirectory);
49 | foreach (var directory in commandSetDirectories)
50 | {
51 | // Skip special folders or hidden folders
52 | if (Path.GetFileName(directory).StartsWith("."))
53 | continue;
54 | string commandJsonPath = Path.Combine(directory, "command.json");
55 | // If there's a command.json, load it
56 | if (File.Exists(commandJsonPath))
57 | {
58 | string commandJson = File.ReadAllText(commandJsonPath);
59 | var commandSetData = JsonConvert.DeserializeObject(commandJson);
60 | if (commandSetData != null)
61 | {
62 | var newCommandSet = new CommandSet
63 | {
64 | Name = commandSetData.Name,
65 | Description = commandSetData.Description,
66 | Commands = new List()
67 | };
68 |
69 | // 检测支持的Revit版本 - 从年份子文件夹确定
70 | List supportedVersions = new List();
71 | var versionDirectories = Directory.GetDirectories(directory)
72 | .Select(Path.GetFileName)
73 | .Where(name => int.TryParse(name, out _))
74 | .ToList();
75 |
76 | // Loop through each command
77 | foreach (var command in commandSetData.Commands)
78 | {
79 | // 创建一个命令配置,但通过检查文件夹确定支持的版本
80 | List supportedCommandVersions = new List();
81 | string dllBasePath = null;
82 |
83 | foreach (var version in versionDirectories)
84 | {
85 | string versionDirectory = Path.Combine(directory, version);
86 | string versionDllPath = null;
87 |
88 | if (!string.IsNullOrEmpty(command.AssemblyPath))
89 | {
90 | // 如果指定了相对路径,在版本子文件夹中查找
91 | versionDllPath = Path.Combine(versionDirectory, command.AssemblyPath);
92 | if (File.Exists(versionDllPath))
93 | {
94 | // 记录基本路径模板
95 | if (dllBasePath == null)
96 | {
97 | // 提取相对路径,用于创建模板
98 | dllBasePath = Path.Combine(commandSetData.Name, "{VERSION}", command.AssemblyPath);
99 | }
100 | supportedCommandVersions.Add(version);
101 | }
102 | }
103 | else
104 | {
105 | // 如果没有指定路径,在版本子文件夹中查找任意DLL
106 | var dllFiles = Directory.GetFiles(versionDirectory, "*.dll");
107 | if (dllFiles.Length > 0)
108 | {
109 | versionDllPath = dllFiles[0]; // 使用找到的第一个DLL
110 | if (dllBasePath == null)
111 | {
112 | // 提取DLL文件名
113 | string dllFileName = Path.GetFileName(versionDllPath);
114 | dllBasePath = Path.Combine(commandSetData.Name, "{VERSION}", dllFileName);
115 | }
116 | supportedCommandVersions.Add(version);
117 | }
118 | }
119 | }
120 |
121 | // 如果至少有一个版本支持此命令
122 | if (supportedCommandVersions.Count > 0 && dllBasePath != null)
123 | {
124 | // 创建命令配置
125 | var commandConfig = new CommandConfig
126 | {
127 | CommandName = command.CommandName,
128 | Description = command.Description,
129 | // 使用带有版本占位符的路径
130 | AssemblyPath = dllBasePath,
131 | Enabled = false,
132 | // 记录所有支持的版本
133 | SupportedRevitVersions = supportedCommandVersions.ToArray()
134 | };
135 |
136 | // 添加到命令列表
137 | newCommandSet.Commands.Add(commandConfig);
138 | availableCommandNames.Add(command.CommandName);
139 | }
140 | }
141 |
142 | // 如果有可用命令,添加到命令集列表
143 | if (newCommandSet.Commands.Any())
144 | {
145 | availableCommandSets[commandSetData.Name] = newCommandSet;
146 | }
147 | }
148 | }
149 | }
150 | // 2. Load registry, update command enabled status, and clean up non-existent commands
151 | if (File.Exists(registryFilePath))
152 | {
153 | string registryJson = File.ReadAllText(registryFilePath);
154 | var registry = JsonConvert.DeserializeObject(registryJson);
155 | if (registry?.Commands != null)
156 | {
157 | // Keep only valid commands
158 | List validCommands = new List();
159 | foreach (var registryItem in registry.Commands)
160 | {
161 | if (availableCommandNames.Contains(registryItem.CommandName))
162 | {
163 | validCommands.Add(registryItem);
164 | // Update the enabled status of this command in all command sets
165 | foreach (var commandSet in availableCommandSets.Values)
166 | {
167 | var command = commandSet.Commands.FirstOrDefault(c => c.CommandName == registryItem.CommandName);
168 | if (command != null)
169 | {
170 | command.Enabled = registryItem.Enabled;
171 | }
172 | }
173 | }
174 | }
175 | // If there are invalid commands, update the registry file
176 | if (validCommands.Count != registry.Commands.Count)
177 | {
178 | registry.Commands = validCommands;
179 | string updatedJson = JsonConvert.SerializeObject(registry, Formatting.Indented);
180 | File.WriteAllText(registryFilePath, updatedJson);
181 | }
182 | }
183 | }
184 | // 3. Add command sets to the UI collection
185 | foreach (var commandSet in availableCommandSets.Values)
186 | {
187 | commandSets.Add(commandSet);
188 | }
189 | // If no command sets found, display a message
190 | if (commandSets.Count == 0)
191 | {
192 | MessageBox.Show("No command sets found. Please check if the Commands folder exists and contains valid command sets.",
193 | "No Command Sets", MessageBoxButton.OK, MessageBoxImage.Information);
194 | }
195 | }
196 | catch (Exception ex)
197 | {
198 | MessageBox.Show($"Error loading command sets: {ex.Message}", "Error",
199 | MessageBoxButton.OK, MessageBoxImage.Error);
200 | }
201 | }
202 |
203 | private void CommandSetListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
204 | {
205 | currentCommands.Clear();
206 | var selectedCommandSet = CommandSetListBox.SelectedItem as CommandSet;
207 | if (selectedCommandSet != null)
208 | {
209 | NoSelectionTextBlock.Visibility = Visibility.Collapsed;
210 | FeaturesHeaderTextBlock.Text = $"{selectedCommandSet.Name} - Command List";
211 | // Load commands from selected command set
212 | foreach (var command in selectedCommandSet.Commands)
213 | {
214 | currentCommands.Add(command);
215 | }
216 | }
217 | else
218 | {
219 | NoSelectionTextBlock.Visibility = Visibility.Visible;
220 | FeaturesHeaderTextBlock.Text = "Command List";
221 | }
222 | }
223 |
224 | private void RefreshButton_Click(object sender, RoutedEventArgs e)
225 | {
226 | // Save current selection state
227 | var selectedIndex = CommandSetListBox.SelectedIndex;
228 | // Reload command sets
229 | LoadCommandSets();
230 | // Restore selection
231 | if (selectedIndex >= 0 && selectedIndex < commandSets.Count)
232 | {
233 | CommandSetListBox.SelectedIndex = selectedIndex;
234 | }
235 | }
236 |
237 | private void SelectAllButton_Click(object sender, RoutedEventArgs e)
238 | {
239 | // Only operate on the currently displayed commands
240 | if (currentCommands.Count > 0)
241 | {
242 | foreach (var command in currentCommands)
243 | {
244 | command.Enabled = true;
245 | }
246 |
247 | // Refresh the UI
248 | FeaturesListView.Items.Refresh();
249 | }
250 | }
251 |
252 | private void UnselectAllButton_Click(object sender, RoutedEventArgs e)
253 | {
254 | // Only operate on the currently displayed commands
255 | if (currentCommands.Count > 0)
256 | {
257 | foreach (var command in currentCommands)
258 | {
259 | command.Enabled = false;
260 | }
261 |
262 | // Refresh the UI
263 | FeaturesListView.Items.Refresh();
264 | }
265 | }
266 |
267 | private void SaveButton_Click(object sender, RoutedEventArgs e)
268 | {
269 | try
270 | {
271 | string registryFilePath = PathManager.GetCommandRegistryFilePath();
272 | // 读取现有的注册表以保留完整的命令信息
273 | Dictionary existingCommandsDict = new Dictionary();
274 | if (File.Exists(registryFilePath))
275 | {
276 | string registryJson = File.ReadAllText(registryFilePath);
277 | var existingRegistry = JsonConvert.DeserializeObject(registryJson);
278 | if (existingRegistry?.Commands != null)
279 | {
280 | foreach (var cmd in existingRegistry.Commands)
281 | {
282 | existingCommandsDict[cmd.CommandName] = cmd;
283 | }
284 | }
285 | }
286 | // 创建新的registry对象
287 | CommandRegistryJson registry = new CommandRegistryJson();
288 | registry.Commands = new List();
289 | // 收集所有"已启用"的命令
290 | foreach (var commandSet in commandSets)
291 | {
292 | // 尝试从command.json中获取开发者信息
293 | var commandSetDeveloper = new DeveloperInfo { Name = "Unspecified", Email = "Unspecified" };
294 | string commandJsonPath = Path.Combine(PathManager.GetCommandsDirectoryPath(),
295 | commandSet.Name, "command.json");
296 | if (File.Exists(commandJsonPath))
297 | {
298 | try
299 | {
300 | string commandJson = File.ReadAllText(commandJsonPath);
301 | var commandSetData = JsonConvert.DeserializeObject(commandJson);
302 | if (commandSetData != null)
303 | {
304 | commandSetDeveloper = commandSetData.Developer ?? commandSetDeveloper;
305 | }
306 | }
307 | catch { /* 如果解析失败,使用默认值 */ }
308 | }
309 |
310 | foreach (var command in commandSet.Commands)
311 | {
312 | // 只添加启用的命令到注册表
313 | if (command.Enabled)
314 | {
315 | CommandConfig newConfig;
316 | // 检查命令是否已经存在于之前的注册表中
317 | if (existingCommandsDict.ContainsKey(command.CommandName))
318 | {
319 | // 如果存在,保留原有信息,只更新启用状态和路径模板
320 | newConfig = existingCommandsDict[command.CommandName];
321 | newConfig.Enabled = true;
322 | newConfig.AssemblyPath = command.AssemblyPath;
323 | newConfig.SupportedRevitVersions = command.SupportedRevitVersions;
324 | }
325 | else
326 | {
327 | // 如果是新命令,创建新配置
328 | newConfig = new CommandConfig
329 | {
330 | CommandName = command.CommandName,
331 | AssemblyPath = command.AssemblyPath ?? "",
332 | Enabled = true,
333 | Description = command.Description,
334 | SupportedRevitVersions = command.SupportedRevitVersions,
335 | Developer = commandSetDeveloper
336 | };
337 | }
338 | registry.Commands.Add(newConfig);
339 | }
340 | }
341 | }
342 | // 构建摘要以显示
343 | string enabledFeaturesText = "";
344 | int enabledCount = registry.Commands.Count;
345 | foreach (var command in registry.Commands)
346 | {
347 | string commandSetName = commandSets
348 | .FirstOrDefault(cs => cs.Commands.Any(c => c.CommandName == command.CommandName))?.Name ?? "Unknown";
349 | string versions = command.SupportedRevitVersions != null && command.SupportedRevitVersions.Any()
350 | ? $" (Revit {string.Join(", ", command.SupportedRevitVersions)})"
351 | : "";
352 | enabledFeaturesText += $"• {commandSetName}: {command.CommandName}\n";
353 | }
354 | // 序列化并保存到文件
355 | string json = JsonConvert.SerializeObject(registry, Formatting.Indented);
356 | File.WriteAllText(registryFilePath, json);
357 | MessageBox.Show($"Command set settings successfully saved!\n\nEnabled {enabledCount} commands:\n{enabledFeaturesText}",
358 | "Settings Saved", MessageBoxButton.OK, MessageBoxImage.Information);
359 | }
360 | catch (Exception ex)
361 | {
362 | MessageBox.Show($"Error saving settings: {ex.Message}", "Error",
363 | MessageBoxButton.OK, MessageBoxImage.Error);
364 | }
365 | }
366 |
367 | private void OpenFolderButton_Click(object sender, RoutedEventArgs e)
368 | {
369 | try
370 | {
371 | Process.Start("explorer.exe", PathManager.GetCommandsDirectoryPath());
372 | }
373 | catch (Exception ex)
374 | {
375 | MessageBox.Show($"Error opening Commands folder: {ex.Message}", "Error",
376 | MessageBoxButton.OK, MessageBoxImage.Error);
377 | }
378 | }
379 | }
380 |
381 | // Data models
382 | public class CommandSet
383 | {
384 | public string Name { get; set; }
385 | public string Description { get; set; }
386 | public List Commands { get; set; } = new List();
387 | }
388 | // Configuration files
389 | public class CommandRegistryJson
390 | {
391 | public List Commands { get; set; } = new List();
392 | }
393 |
394 | public class CommandJson
395 | {
396 | public string Name { get; set; }
397 | public string Description { get; set; }
398 | public List Commands { get; set; } = new List();
399 | public DeveloperInfo Developer { get; set; }
400 | public List SupportedRevitVersions { get; set; } = new List();
401 | }
402 |
403 | public class CommandItemJson
404 | {
405 | public string CommandName { get; set; }
406 | public string Description { get; set; }
407 | public string AssemblyPath { get; set; }
408 | }
409 | }
--------------------------------------------------------------------------------
/revit-mcp-plugin/UI/SettingsWindow.xaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/revit-mcp-plugin/UI/SettingsWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using System.Windows;
7 | using System.Windows.Controls;
8 | using System.Windows.Data;
9 | using System.Windows.Documents;
10 | using System.Windows.Input;
11 | using System.Windows.Media;
12 | using System.Windows.Media.Imaging;
13 | using System.Windows.Navigation;
14 | using System.Windows.Shapes;
15 |
16 | namespace revit_mcp_plugin.UI
17 | {
18 | ///
19 | /// Settings.xaml 的交互逻辑
20 | ///
21 | public partial class SettingsWindow : Window
22 | {
23 | private CommandSetSettingsPage commandSetPage;
24 | private bool isInitialized = false;
25 |
26 | public SettingsWindow()
27 | {
28 | InitializeComponent();
29 |
30 | // 初始化页面
31 | commandSetPage = new CommandSetSettingsPage();
32 |
33 | // 加载默认页面
34 | ContentFrame.Navigate(commandSetPage);
35 |
36 | isInitialized = true;
37 | }
38 |
39 | private void NavListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
40 | {
41 | if (!isInitialized) return;
42 |
43 | if (NavListBox.SelectedItem == CommandSetItem)
44 | {
45 | ContentFrame.Navigate(commandSetPage);
46 | }
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/revit-mcp-plugin/Utils/Logger.cs:
--------------------------------------------------------------------------------
1 | using RevitMCPSDK.API.Interfaces;
2 | using System;
3 | using System.IO;
4 |
5 | namespace revit_mcp_plugin.Utils
6 | {
7 | public class Logger : ILogger
8 | {
9 | private readonly string _logFilePath;
10 | private LogLevel _currentLogLevel = LogLevel.Info;
11 |
12 | public Logger()
13 | {
14 | _logFilePath = Path.Combine(PathManager.GetLogsDirectoryPath(), $"mcp_{DateTime.Now:yyyyMMdd}.log");
15 |
16 | }
17 |
18 | public void Log(LogLevel level, string message, params object[] args)
19 | {
20 | if (level < _currentLogLevel)
21 | return;
22 |
23 | string formattedMessage = args.Length > 0 ? string.Format(message, args) : message;
24 | string logEntry = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [{level}] {formattedMessage}";
25 |
26 | // 输出到 Debug 窗口
27 | System.Diagnostics.Debug.WriteLine(logEntry);
28 |
29 | // 写入日志文件
30 | try
31 | {
32 | File.AppendAllText(_logFilePath, logEntry + Environment.NewLine);
33 | }
34 | catch
35 | {
36 | // 如果写入日志文件失败,不抛出异常
37 | }
38 | }
39 |
40 | public void Debug(string message, params object[] args)
41 | {
42 | Log(LogLevel.Debug, message, args);
43 | }
44 |
45 | public void Info(string message, params object[] args)
46 | {
47 | Log(LogLevel.Info, message, args);
48 | }
49 |
50 | public void Warning(string message, params object[] args)
51 | {
52 | Log(LogLevel.Warning, message, args);
53 | }
54 |
55 | public void Error(string message, params object[] args)
56 | {
57 | Log(LogLevel.Error, message, args);
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/revit-mcp-plugin/Utils/PathManager.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace revit_mcp_plugin.Utils
10 | {
11 | public static class PathManager
12 | {
13 | ///
14 | /// Gets the root application data directory
15 | ///
16 | public static string GetAppDataDirectoryPath()
17 | {
18 | string applicationPath = System.Reflection.Assembly.GetExecutingAssembly().Location;
19 | string applicationDirectory = Path.GetDirectoryName(applicationPath);
20 |
21 | return applicationDirectory;
22 | }
23 | ///
24 | /// Gets the path to the Commands directory
25 | ///
26 | public static string GetCommandsDirectoryPath()
27 | {
28 | string appDataDirectory = GetAppDataDirectoryPath();
29 | string commandsDirectory = Path.Combine(appDataDirectory, "Commands");
30 |
31 | EnsureDirectoryExists(commandsDirectory);
32 |
33 | return commandsDirectory;
34 | }
35 | ///
36 | /// Gets the path to the Logs directory
37 | ///
38 | public static string GetLogsDirectoryPath()
39 | {
40 | string appDataDirectory = GetAppDataDirectoryPath();
41 | string logsDirectory = Path.Combine(appDataDirectory, "Logs");
42 |
43 | EnsureDirectoryExists(logsDirectory);
44 |
45 | return logsDirectory;
46 | }
47 | ///
48 | /// Gets the path to the command registry file.
49 | /// If the file doesn't exist, creates it with default content.
50 | ///
51 | /// Whether to create a default file if it doesn't exist (default: true)
52 | /// Path to the command registry file
53 | public static string GetCommandRegistryFilePath(bool createIfNotExists = true)
54 | {
55 | string commandsDirectory = GetCommandsDirectoryPath();
56 | string registryFilePath = Path.Combine(commandsDirectory, "commandRegistry.json");
57 |
58 | if (createIfNotExists && !File.Exists(registryFilePath))
59 | {
60 | CreateDefaultCommandRegistryFile(registryFilePath);
61 | }
62 |
63 | return registryFilePath;
64 | }
65 | ///
66 | /// Creates a default command registry file with empty commands array
67 | ///
68 | /// Path where to create the file
69 | private static void CreateDefaultCommandRegistryFile(string filePath)
70 | {
71 | try
72 | {
73 | var defaultRegistry = new { commands = new object[] { } };
74 | string jsonContent = JsonConvert.SerializeObject(defaultRegistry, Formatting.Indented);
75 |
76 | File.WriteAllText(filePath, jsonContent);
77 | }
78 | catch (Exception ex)
79 | {
80 | Console.WriteLine($"Error creating default command registry file: {ex.Message}");
81 | }
82 | }
83 | ///
84 | /// Ensures that the specified directory exists
85 | ///
86 | /// The path to check and create if needed
87 | private static void EnsureDirectoryExists(string directoryPath)
88 | {
89 | if (!Directory.Exists(directoryPath))
90 | {
91 | Directory.CreateDirectory(directoryPath);
92 | }
93 | }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/revit-mcp-plugin/revit-mcp-plugin.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Library
4 | revit_mcp_plugin
5 | false
6 | true
7 | true
8 | true
9 | Debug R20;Debug R21;Debug R22;Debug R24;Debug R25;Release R20;Release R21;Release R22;Release R23;Release R24;Release R25;Debug R23
10 | x64
11 | false
12 |
13 |
14 |
15 | 2020
16 | net48
17 | bin\$(Configuration.Split(' ')[0])\$(RevitVersion)\
18 |
19 |
20 | 2021
21 | net48
22 | bin\$(Configuration.Split(' ')[0])\$(RevitVersion)\
23 |
24 |
25 | 2022
26 | net48
27 | bin\$(Configuration.Split(' ')[0])\$(RevitVersion)\
28 |
29 |
30 | 2023
31 | net48
32 | bin\$(Configuration.Split(' ')[0])\$(RevitVersion)\
33 |
34 |
35 | 2024
36 | net48
37 | bin\$(Configuration.Split(' ')[0])\$(RevitVersion)\
38 |
39 |
40 | 2025
41 | net8.0-windows10.0.19041.0
42 | bin\$(Configuration.Split(' ')[0])\$(RevitVersion)\
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/revit-mcp-plugin/revit-mcp.addin:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | revit-mcp
5 | revit-mcp-plugin.dll
6 | revit_mcp_plugin.Core.Application
7 | 090A4C8C-61DC-426D-87DF-E4BAE0F80EC1
8 | revit-mcp
9 | https://github.com/revit-mcp/revit-mcp
10 |
11 |
--------------------------------------------------------------------------------