-
Notifications
You must be signed in to change notification settings - Fork 45
New Commands #13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
New Commands #13
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request is part 3 of the Sparx-Fire contributions, adding Revit 2026 build support, fixing API compatibility issues related to ElementId changes across Revit versions, and introducing new architectural commands for room and level management.
Changes:
- Adds Revit 2026 (R26) build configuration with appropriate preprocessor directives
- Introduces ElementIdExtensions utility for cross-version ElementId API compatibility
- Adds new commands: say_hello (test), create_room, tag_rooms, create_level
- Registers previously existing but unregistered commands (get_selected_elements, create_point_based_element, create_surface_based_element, color_splash, ai_element_filter, operate_element)
- Renames tag_all_walls to tag_walls, removes create_Wall, re-enables delete_element
- Fixes ElementId API compatibility across all service handlers to use extension methods
- Adds CeilingType support to GetAvailableFamilyTypesEventHandler
- Updates Floor.Create preprocessor directive from REVIT2022_OR_GREATER to REVIT2023_OR_GREATER
Reviewed changes
Copilot reviewed 26 out of 26 changed files in this pull request and generated 34 comments.
Show a summary per file
| File | Description |
|---|---|
| RevitMCPCommandSet.csproj | Adds R26 configuration and preprocessor defines for version compatibility |
| Utils/ElementIdExtensions.cs | New utility providing GetValue()/GetIntValue() for ElementId cross-version compatibility |
| Utils/ProjectUtils.cs | Updates to use GetIntValue() extension method |
| Services/TagWallsEventHandler.cs | Adds Utils import for ElementIdExtensions |
| Services/TagRoomsEventHandler.cs | New event handler for tagging rooms (uses preprocessor blocks instead of extension methods) |
| Services/SayHelloEventHandler.cs | New simple test event handler displaying greeting dialog |
| Services/GetAvailableFamilyTypesEventHandler.cs | Adds CeilingType to available system types |
| Services/DeleteElementEventHandler.cs | Uncommented/re-enabled previously disabled code |
| Services/CreateSurfaceElementEventHandler.cs | Updates Floor.Create directive to REVIT2023_OR_GREATER and uses GetIntValue() |
| Services/CreatePointElementEventHandler.cs | Updates to use GetIntValue() extension method |
| Services/CreateLineElementEventHandler.cs | Updates to use GetIntValue() extension method |
| Services/ColorSplashEventHandler.cs | Updates directive to REVIT2023_OR_GREATER, still uses direct Id access |
| Services/Architecture/CreateRoomEventHandler.cs | New handler for creating rooms (uses preprocessor blocks, missing Utils import) |
| Services/Architecture/CreateLevelEventHandler.cs | New handler for creating levels (uses preprocessor blocks, missing Utils import) |
| Services/AnnotationComponents/CreateDimensionEventHandler.cs | Updates to use GetIntValue() and adds Utils import |
| Services/AIElementFilterEventHandler.cs | Updates all ElementId accesses to use GetIntValue()/GetValue() extension methods |
| Commands/Test/SayHelloCommand.cs | New command for test greeting dialog |
| Commands/TagWallsCommand.cs | Renames command from tag_all_walls to tag_walls |
| Commands/TagRoomsCommand.cs | New command for tagging rooms |
| Commands/Delete/DeleteElementCommand.cs | Uncommented/re-enabled previously disabled command |
| Commands/Architecture/CreateRoomCommand.cs | New command for creating rooms |
| Commands/Architecture/CreateLevelCommand.cs | New command for creating levels |
| Models/Common/PointElement.cs | Removes BOM, adds proper closing brace |
| Models/Architecture/RoomCreationInfo.cs | Adds new properties: upperLimitId, limitOffset, baseOffset, department, comments |
| Models/Architecture/LevelInfo.cs | Adds createFloorPlan and createCeilingPlan boolean properties |
| command.json | Updates command registrations, removes create_Wall, renames tag_all_walls to tag_walls, adds new commands |
Comments suppressed due to low confidence (1)
revit-mcp-commandset/Services/AIElementFilterEventHandler.cs:638
- This foreach loop implicitly filters its target sequence - consider filtering the sequence explicitly using '.Where(...)'.
foreach (UIView uiView in openViews)
{
// 检查视图是否打开
if (uiView.ViewId.GetValue() == view.Id.GetValue())
{
info.IsOpen = true;
// 检查视图是否是当前激活的视图
if (uidoc.ActiveView.Id.GetValue() == view.Id.GetValue())
{
info.IsActive = true;
}
break;
}
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| #if REVIT2024_OR_GREATER | ||
| // If specific tag type ID was specified, try to use it | ||
| if (!string.IsNullOrEmpty(_tagTypeId)) | ||
| { | ||
| if (int.TryParse(_tagTypeId, out int id)) | ||
| { | ||
| ElementId elementId = new ElementId(id); | ||
| Element element = doc.GetElement(elementId); | ||
|
|
||
| if (element != null && element is FamilySymbol symbol && | ||
| symbol.Category != null && | ||
| symbol.Category.Id.Value == (int)BuiltInCategory.OST_RoomTags) | ||
| { | ||
| return symbol; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Find the first available room tag type | ||
| FilteredElementCollector tagCollector = new FilteredElementCollector(doc); | ||
| FamilySymbol roomTagType = tagCollector.OfClass(typeof(FamilySymbol)) | ||
| .WhereElementIsElementType() | ||
| .Where(e => e.Category != null && | ||
| e.Category.Id.Value == (int)BuiltInCategory.OST_RoomTags) | ||
| .Cast<FamilySymbol>() | ||
| .FirstOrDefault(); | ||
|
|
||
| return roomTagType; | ||
| #else | ||
| // If specific tag type ID was specified, try to use it | ||
| if (!string.IsNullOrEmpty(_tagTypeId)) | ||
| { | ||
| if (int.TryParse(_tagTypeId, out int id)) | ||
| { | ||
| ElementId elementId = new ElementId(id); | ||
| Element element = doc.GetElement(elementId); | ||
|
|
||
| if (element != null && element is FamilySymbol symbol && | ||
| symbol.Category != null && | ||
| symbol.Category.Id.IntegerValue == (int)BuiltInCategory.OST_RoomTags) | ||
| { | ||
| return symbol; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Find the first available room tag type | ||
| FilteredElementCollector tagCollector = new FilteredElementCollector(doc); | ||
| FamilySymbol roomTagType = tagCollector.OfClass(typeof(FamilySymbol)) | ||
| .WhereElementIsElementType() | ||
| .Where(e => e.Category != null && | ||
| e.Category.Id.IntegerValue == (int)BuiltInCategory.OST_RoomTags) | ||
| .Cast<FamilySymbol>() | ||
| .FirstOrDefault(); | ||
|
|
||
| return roomTagType; | ||
| #endif |
Copilot
AI
Feb 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use the GetIntValue() extension method instead of direct Id.Value and Id.IntegerValue access within preprocessor blocks. Replace the preprocessor conditionals on lines 447-503 with symbol.Category.Id.GetIntValue() for consistency with the rest of the codebase.
| #if REVIT2024_OR_GREATER | ||
| Id = (int)room.Id.Value, | ||
| #else | ||
| Id = room.Id.IntegerValue, | ||
| #endif |
Copilot
AI
Feb 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use the GetIntValue() extension method instead of preprocessor blocks. Replace lines 252-256 with Id = room.Id.GetIntValue() for consistency with the codebase pattern.
| #if REVIT2024_OR_GREATER | |
| Id = (int)room.Id.Value, | |
| #else | |
| Id = room.Id.IntegerValue, | |
| #endif | |
| Id = room.Id.GetIntValue(), |
| #if REVIT2024_OR_GREATER | ||
| Id = (int)existingLevel.Id.Value, | ||
| #else | ||
| Id = existingLevel.Id.IntegerValue, | ||
| #endif |
Copilot
AI
Feb 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use the GetIntValue() extension method instead of preprocessor blocks. Replace the preprocessor block with Id = existingLevel.Id.GetIntValue() for consistency with the codebase pattern.
| #if REVIT2024_OR_GREATER | |
| Id = (int)existingLevel.Id.Value, | |
| #else | |
| Id = existingLevel.Id.IntegerValue, | |
| #endif | |
| Id = existingLevel.Id.GetIntValue(), |
| #if REVIT2024_OR_GREATER | ||
| ElementId id = parameter.AsElementId(); | ||
| if (id == ElementId.InvalidElementId) | ||
| return "None"; | ||
|
|
||
| Element element = doc.GetElement(id); | ||
| return element?.Name ?? id.Value.ToString(); | ||
| #else | ||
| ElementId id = parameter.AsElementId(); | ||
| if (id == ElementId.InvalidElementId) | ||
| return "None"; | ||
|
|
||
| Element element = doc.GetElement(id); | ||
| return element?.Name ?? id.IntegerValue.ToString(); | ||
| #endif |
Copilot
AI
Feb 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use the GetValue() extension method for consistency. Replace id.Value.ToString() on line 270 and id.IntegerValue.ToString() on line 277 with id.GetValue().ToString() to use the extension method pattern consistently throughout the codebase.
| using Autodesk.Revit.DB.Architecture; | ||
| using Autodesk.Revit.UI; | ||
| using RevitMCPCommandSet.Models.Architecture; | ||
| using RevitMCPCommandSet.Models.Common; |
Copilot
AI
Feb 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add using RevitMCPCommandSet.Utils; import statement. This is needed to access the ElementIdExtensions methods (GetValue/GetIntValue) that should be used instead of the preprocessor blocks for ElementId value access.
| using RevitMCPCommandSet.Models.Common; | |
| using RevitMCPCommandSet.Models.Common; | |
| using RevitMCPCommandSet.Utils; |
| List<RoomCreationInfo> data = new List<RoomCreationInfo>(); | ||
| // Parse parameters | ||
| data = parameters["data"].ToObject<List<RoomCreationInfo>>(); |
Copilot
AI
Feb 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This assignment to data is useless, since its value is never read.
| List<RoomCreationInfo> data = new List<RoomCreationInfo>(); | |
| // Parse parameters | |
| data = parameters["data"].ToObject<List<RoomCreationInfo>>(); | |
| // Parse parameters | |
| List<RoomCreationInfo> data = parameters["data"].ToObject<List<RoomCreationInfo>>(); |
| } | ||
| } | ||
|
|
||
| string message = $"Successfully processed {createdLevels.Count} level(s)"; |
Copilot
AI
Feb 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This assignment to message is useless, since its value is never read.
| string message = $"Successfully processed {createdLevels.Count} level(s)"; | |
| string message; |
| if (digits.Length > 0 && int.TryParse(digits, out int trailingNum)) | ||
| { | ||
| if (trailingNum > maxNumber) maxNumber = trailingNum; | ||
| } |
Copilot
AI
Feb 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These 'if' statements can be combined.
| if (!string.IsNullOrEmpty(_tagTypeId)) | ||
| { | ||
| if (int.TryParse(_tagTypeId, out int id)) | ||
| { | ||
| ElementId elementId = new ElementId(id); | ||
| Element element = doc.GetElement(elementId); | ||
|
|
||
| if (element != null && element is FamilySymbol symbol && | ||
| symbol.Category != null && | ||
| symbol.Category.Id.IntegerValue == (int)BuiltInCategory.OST_RoomTags) | ||
| { | ||
| return symbol; | ||
| } | ||
| } | ||
| } |
Copilot
AI
Feb 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These 'if' statements can be combined.
| string roomNumber = roomInfo.Number; | ||
| if (!string.IsNullOrEmpty(roomNumber)) | ||
| { | ||
| // User provided a number - make it unique if it already exists | ||
| roomNumber = GetUniqueRoomNumber(roomNumber, existingRoomNumbers); | ||
| } | ||
| else | ||
| { | ||
| // No number provided - generate next available number (don't use room.Number) | ||
| roomNumber = GetNextAvailableRoomNumber(existingRoomNumbers); | ||
| } |
Copilot
AI
Feb 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both branches of this 'if' statement write to the same variable - consider using '?' to express intent better.
| string roomNumber = roomInfo.Number; | |
| if (!string.IsNullOrEmpty(roomNumber)) | |
| { | |
| // User provided a number - make it unique if it already exists | |
| roomNumber = GetUniqueRoomNumber(roomNumber, existingRoomNumbers); | |
| } | |
| else | |
| { | |
| // No number provided - generate next available number (don't use room.Number) | |
| roomNumber = GetNextAvailableRoomNumber(existingRoomNumbers); | |
| } | |
| string roomNumber = !string.IsNullOrEmpty(roomInfo.Number) | |
| ? GetUniqueRoomNumber(roomInfo.Number, existingRoomNumbers) | |
| : GetNextAvailableRoomNumber(existingRoomNumbers); |
Hi 👋,
This is part 3 of the Sparx-Fire contributions to the mcp-servers-for-revit project. Please see the extended description in part 1 for the full context of these changes.
This PR adds a build configuration for Revit 2026, fixes some build errors, and adds support for additional commands (matching the PR in the MCP server repo):