Skip to content

Conversation

@bobbyg603
Copy link

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):

  • New Commands
    • say_hello - Test greeting dialog
    • create_room - Create and place rooms with custom names, numbers, and properties
    • tag_rooms - Tag all rooms in the current view with room name and number
    • create_level - Create levels at specified elevations with automatic floor plan generation
  • Newly Registered Commands (code existed, now wired up in command.json)
    • get_selected_elements
    • create_point_based_element
    • create_surface_based_element
    • color_splash
    • ai_element_filter
    • operate_element
  • Modified Commands
    • tag_all_walls renamed to tag_walls
    • create_Wall removed (superseded by create_line_based_element)
    • delete_element re-enabled (was commented out)
  • Build
    • Added Revit 2026 (R26) support
    • Added ElementIdExtensions for cross-version API compatibility

@bobbyg603 bobbyg603 changed the title Feature/new commands New Commands Feb 10, 2026
@jmcouffin jmcouffin requested a review from Copilot February 11, 2026 17:57
Copy link

Copilot AI left a 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

                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.

Comment on lines +447 to +503
#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
Copy link

Copilot AI Feb 11, 2026

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.

Copilot uses AI. Check for mistakes.
Comment on lines +252 to +256
#if REVIT2024_OR_GREATER
Id = (int)room.Id.Value,
#else
Id = room.Id.IntegerValue,
#endif
Copy link

Copilot AI Feb 11, 2026

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.

Suggested change
#if REVIT2024_OR_GREATER
Id = (int)room.Id.Value,
#else
Id = room.Id.IntegerValue,
#endif
Id = room.Id.GetIntValue(),

Copilot uses AI. Check for mistakes.
Comment on lines +82 to +86
#if REVIT2024_OR_GREATER
Id = (int)existingLevel.Id.Value,
#else
Id = existingLevel.Id.IntegerValue,
#endif
Copy link

Copilot AI Feb 11, 2026

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.

Suggested change
#if REVIT2024_OR_GREATER
Id = (int)existingLevel.Id.Value,
#else
Id = existingLevel.Id.IntegerValue,
#endif
Id = existingLevel.Id.GetIntValue(),

Copilot uses AI. Check for mistakes.
Comment on lines 264 to 278
#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
Copy link

Copilot AI Feb 11, 2026

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.

Copilot uses AI. Check for mistakes.
using Autodesk.Revit.DB.Architecture;
using Autodesk.Revit.UI;
using RevitMCPCommandSet.Models.Architecture;
using RevitMCPCommandSet.Models.Common;
Copy link

Copilot AI Feb 11, 2026

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.

Suggested change
using RevitMCPCommandSet.Models.Common;
using RevitMCPCommandSet.Models.Common;
using RevitMCPCommandSet.Utils;

Copilot uses AI. Check for mistakes.
Comment on lines +34 to +36
List<RoomCreationInfo> data = new List<RoomCreationInfo>();
// Parse parameters
data = parameters["data"].ToObject<List<RoomCreationInfo>>();
Copy link

Copilot AI Feb 11, 2026

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.

Suggested change
List<RoomCreationInfo> data = new List<RoomCreationInfo>();
// Parse parameters
data = parameters["data"].ToObject<List<RoomCreationInfo>>();
// Parse parameters
List<RoomCreationInfo> data = parameters["data"].ToObject<List<RoomCreationInfo>>();

Copilot uses AI. Check for mistakes.
}
}

string message = $"Successfully processed {createdLevels.Count} level(s)";
Copy link

Copilot AI Feb 11, 2026

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.

Suggested change
string message = $"Successfully processed {createdLevels.Count} level(s)";
string message;

Copilot uses AI. Check for mistakes.
Comment on lines +343 to +346
if (digits.Length > 0 && int.TryParse(digits, out int trailingNum))
{
if (trailingNum > maxNumber) maxNumber = trailingNum;
}
Copy link

Copilot AI Feb 11, 2026

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.

Copilot uses AI. Check for mistakes.
Comment on lines +477 to +491
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;
}
}
}
Copy link

Copilot AI Feb 11, 2026

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.

Copilot uses AI. Check for mistakes.
Comment on lines +177 to +187
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);
}
Copy link

Copilot AI Feb 11, 2026

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.

Suggested change
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);

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants