-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathLLM.txt
More file actions
727 lines (523 loc) · 29.7 KB
/
LLM.txt
File metadata and controls
727 lines (523 loc) · 29.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
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
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
# Simple Revit MCP
## **Context** |
This is a context document for LLMs tasked with developing functionalities for the Revit MCP Python implementation.
## **Terms**:
- **MCP**: The Model Context Protocol (MCP) is an open protocol that enables seamless integration between LLM applications and external data sources and tools.
- **pyRevit**: Revit add-on that allows you to interact with the Revit environment and using its APIs using Python. Based on **IronPython** (which is an implementation of Python 2) to interact with the Revit API, as Revit's API is .NET based and IronPython runs on the .NET framework.
- **Routes Module**: "The new Routes python module, is an HTTP micro-framework to create web APIs running over Revit instances. This means that you can create functionality that could be triggered from remote. This framework provides the necessary mechanism to create a back-end that has access to Revit context."
---
## **Project Architecture** |
This implementation uses a **modular architecture** that separates concerns and makes it easy to add new functionality:
### **Project Structure**
```
revit-mcp-python/
├── main.py # MCP Server (FastMCP implementation)
├── tools/ # MCP Tool definitions (organized by functionality)
│ ├── __init__.py # Tool registration system
│ ├── utils.py # Utility functions for response formatting
│ ├── status_tools.py # Status and connectivity tools
│ ├── model_tools.py # Model information tools
│ ├── view_tools.py # View export and image tools
│ ├── family_tools.py # Family placement and management tools
│ ├── colors_tools.py # Color splashing tools
│ └── code_execution_tools.py # Code execution tools
├── revit-mcp-python.extension/ # pyRevit Extension
│ ├── startup.py # Extension entry point - registers all routes
│ └── revit_mcp/ # Routes API modules (organized by functionality)
│ ├── __init__.py
│ ├── status.py # Health check and status endpoints
│ ├── model_info.py # Model information endpoints
│ ├── views.py # View export endpoints
│ ├── placement.py # Family placement endpoints
│ └── utils.py # Utility functions for IronPython compatibility
│ └── colors.py # Color splashing endpoints
│ └── code_execution.py # Code execution endpoints
├── requirements.txt # Python dependencies
├── pyproject.toml # Project configuration
└── README.md # Project documentation
```
### **Key Architecture Components**
1. **MCP Server (`main.py`)**:
- Built with FastMCP
- Handles HTTP communication with Revit Routes API
- Registers tools from modular tool system
- Provides helper functions for GET/POST/Image requests
2. **pyRevit Extension (`revit-mcp-python.extension/`)**:
- Contains the Routes API that runs inside Revit
- Modular route registration in `startup.py`
- Individual route modules in `revit_mcp/` directory
3. **Tool Registration System (`tools/`)**:
- Modular tool organization by functionality
- Central registration through `tools/__init__.py`
- Unified response formatting through `tools/utils.py`
- Each module registers its own tools with the MCP server
---
## **Routes Documentation** | pyRevit Routes HTTP API Documentation
**Overview**
# pyRevit Routes (HTTP API)
> **Note**: pyRevit Routes is a DRAFT and is subject to change. There is no authentication mechanism implemented yet.
pyRevit Routes is a fairly isolated module under `pyrevitlib/pyrevit/routes/` in pyRevit source. You are encouraged to dive into the code and contribute to the project without affecting the other parts of the pyRevit ecosystem.
## What is pyRevit Routes
Let's say we want to create a web application that would display a list of doors in a model. The web application would be split into two parts:
- **Front-end**: the part that runs in the browser and acts as the user interface, and
- **Back-end**: the part that the front-end contacts to send and receive data
While you are free to select whatever toolchain and GUI framework (React, Vue.js, etc) you are comfortable with for the front-end, the challenge has always been on how to create a back-end that has access to Revit contexts and can query or modify Revit documents. What we really needed is to run a HTTP web server over a running Revit instance and manage the HTTP calls in a way that would be executed in Revit context.
The new Routes python module, is an HTTP micro-framework to create web APIs running over Revit instances. This means that you can create functionality that could be triggered from remote. This framework provides the necessary mechanism to create a back-end that has access to Revit context.
> **Fun fact**: The Runtime REST API is implemented using this framework
## Getting Started
Let's create a simple API using the routes module. One important note here is that it is better to place this API inside your extensions' startup script (`startup.py`) so it only runs once per Revit instance.
### Creating Custom HTTP API Extension
Let's create an empty extension that will define our new HTTP API:
```
MyExtensions/
└── MyHTTPAPI.extension/
└── startup.py
```
Note that the extension does not include anything else other than the `startup.py` script.
Now let's open the `startup.py` and import the routes module. This module provides the functionality we need to define the API:
```python
# first import the routes module
from pyrevit import routes
```
Then we need to define a new API...
### What is a Routes API
Routes API is very similar to a Flask App but at the same time is slightly different. Let's look at the structure of an API end point in pyRevit routes module:
```
http://machine-ip:server-port/routes-api/api-path/...
└── 1 └── 2 └── 3 └── 4
```
- **machine-ip** is the IP of the machine running Revit and the Routes server
- **server-port** is the Routes server port
- **routes-api** is the name of API defined by you. Each program or extension can define their own APIs or extend other APIs as long as the routes do not conflict. This is basically the root path of all the endpoints defined in your Routes API
- **api-path/...** is specific routes define in your API e.g. `doors/`
So with that knowledge, we are going to define our new API. We are using `revit_mcp` as the unique name of our custom API:
```python
api = routes.API('revit_mcp')
```
## API Endpoints
Now that we have the API ready, we can define our API end points. To collect doors from an active Revit model, we would want to collect (HTTP method GET) and the `doors/` in active model using our custom API `revit_mcp/`. So our API endpoint would look like this:
```
GET revit_mcp/doors/
```
Let's define this endpoint. First we need to create a function that collects the doors in the model:
```python
def get_doors(doc):
"""Find doors in given model"""
doors = DB.FilteredElementCollector(doc)\
.OfCategory(DB.BuiltInCategory.OST_Doors)\
.WhereElementIsNotElementType()\
.ToElements()
```
Notice that we have included a `doc` argument for our function since we do not know what the active document would be when this function is being triggered by a HTTP request from outside. So we would need the routes module to provide us the active document as the `doc` argument. See sections below to learn about other predefined arguments that you can define for your logic functions and the implications of each.
Now that we have our logic function, let's tell the Routes module that this is the function we want to be triggered when an HTTP call is made to the `revit_mcp/doors` end point that we have decided on. The `api` object we created, provides handy decorators to make this process very easy:
```python
@api.route('/doors/') #<---- add decorator to the function
def get_doors(doc):
"""Find doors in active model"""
doors = DB.FilteredElementCollector(doc)\
.OfCategory(DB.BuiltInCategory.OST_Doors)\
.WhereElementIsNotElementType()\
.ToElements()
```
You might have noticed that we do not need to include the `revit_mcp` part of the endpoint, since that's the root of our API and all the endpoints defined using `api.route` will end up after the API. So `@api.route('/doors/')` will mark the logic function for `revit_mcp/doors` endpoint.
### Returning Results
Now that we have the logic connected to the endpoint, we can prepare the doors data we have collected as a Response to be sent back to the caller. Let's say we would want to return the Ids of the doors at this point:
```python
@api.route('/doors/')
def get_doors(doc):
"""Find doors in active model"""
doors = DB.FilteredElementCollector(doc)\
.OfCategory(DB.BuiltInCategory.OST_Doors)\
.WhereElementIsNotElementType()\
.ToElements()
# let's grab the integer ids of the door instances
doors_data = [x.Id.IntegerValue for x in doors]
```
and now we can use the routes module again to prepare a response:
```python
@api.route('/doors/')
def get_doors(doc):
"""Find doors in active model"""
doors = DB.FilteredElementCollector(doc)\
.OfCategory(DB.BuiltInCategory.OST_Doors)\
.WhereElementIsNotElementType()\
.ToElements()
doors_data = [x.Id.IntegerValue for x in doors]
# create response and return
return routes.make_response(data=doors_data)
```
We are passing a python list (serializable data type) as the response data to the `routes.make_response()` function. This function has more arguments that you can read about in the sections below but for this example we use the defaults (e.g. HTTP response status defaults to 200 OK).
Well done. Our API endpoint is ready. Let's fire up Revit and the HTTP server now...
## Starting Up The Routes Server
Let's start Revit. The Routes server is disabled by default. After Revit is fully loaded, open pyRevit Settings, and under the Routes section, turn on the server.
The default port for the server is set to `48884`. The first instance of Revit will use the port `48884`, the next Revit instance will use `48885` and so on. See below for information about the pyRevit Routes Core API that could be used to inspect the running Revit instances and their port numbers remotely.
Now Restart Revit. This time Revit will run the `startup.py` in your extension and will attempt to start the Routes Server.
Your operating system might ask you if you want to allow the Routes server (now running inside Revit) to open a connection to the outside and start listening on the predefined port number.
Now that your Revit is fully loaded, and the pyRevit Routes server is also running and authorized to listen on the port, we can test our API endpoint:
1. First open a model that contains a few doors so our API endpoint has some data to return
2. Then go to a different machine on your network, open a browser and type this path in the address bar. Replace the `<machine-ip>` with the IP address of your machine that is running the Revit instance. You can see that in the Example provided under the Routes server status message in pyRevit settings
```
http://<machine-ip>:48884/revit_mcp/doors/
```
You should be able to see a list of door Ids returned as a JSON list object.
## Advanced Routing
### Function Arguments
The predefined arguments listed here can be added to your handler functions if needed. The Routes server will set the appropriate value on these arguments if necessary.
- **request**: a `routes.Request` object will be passed as this argument that contains information about the request that triggered the execution
- **uiapp**: is a reference to the `UIApplication` object provided by Revit runtime
- **uidoc**: is a reference to the `UIDocument` object provided by Revit runtime
- **doc**: is a reference to the active `Document` object provided by Revit runtime
> **IMPORTANT NOTE**: if any of the `uiapp`, `uidoc`, or `doc` arguments are defined in your handler function, the Routes server will assume your handler function needs Revit API context and will execute your function as an External Event using the Revit API. This way your handler function can use transactions to make changes to the Revit document and is guaranteed to be the only function running in API context.
### Defining Routes
The `api.route` decorator can be used to create more complex routes:
#### Request Methods
You can pass the acceptable HTTP methods for your handler function to the decorator as shown below:
```python
@api.route('api/end/point/', methods=["GET", "POST"])
```
The `request` argument on your handler functions can be used to check which HTTP method is being called on the endpoint:
```python
@api.route('api/end/point/method', methods=["GET", "POST"])
def test_method(request):
if request.method == "POST":
# do post stuff
else:
# do get stuff
```
#### Route Parameters
You can define a series of parameters to be extracted from the endpoint path and passed to the handler function.
Define the parameter following the `/<type:name>/` format e.g. `/<int:door_id>/` in the example below:
```python
# /doors/12
# /doors/2313
@api.route('/doors/<int:door_id>')
def post_id(door_id):
# do stuff with door_id
```
A few other data types are supported as well (`int`, `float`, `double`, `bool`, `uuid`):
```python
api.route('/doors/<uuid:door_uniqueid>')
def post_uuid(door_uniqueid):
# do stuff with door_uniqueid
```
#### Request Data
The `request` argument on your handler functions can be used to grab any data being passed to the endpoint:
```python
@api.route('api/end/point/data', methods=["POST"])
def test_data(request):
if request.data:
# process data
```
#### Request Headers
The `request` argument on your handler functions can be used to grab request headers:
```python
@api.route('api/end/point/headers', methods=["POST"])
def test_headers(request):
headers = request.headers # dictionary of header key:value
```
### Returning Results
The `routes.make_response` function can be used to prepare a `routes.Response` object:
```python
@api.route('api/end/point/response', methods=["POST"])
def mirror(request):
return routes.make_response(
data=request.data,
status=routes.HTTP_OK,
headers={"pyRevit": "v4.6.7"}
)
```
### Providing Callback URL
API calls to the Routes server can provide a callback URL when the endpoint handler is going to take a lot of time to complete the work. Include the `callbackUrl` in the data sent by the HTTP request:
```json
{
// your data,
"callbackUrl": "http://mycallbackserver/api/end/point/callback"
}
```
If callback URL is provided, the server will immediately accept the request and will send back status `204 NO_CONTENT`.
When the handler function completes the work, the Routes server will make a POST call to the callback URL and will provide the results of the handler function.
### Exceptions
The Routes server will provide information about any exceptions that occur when running your handler function:
```json
{
"exception": {
"source": "Autodesk Revit 2019.2 build: 20190808_0900(x64)",
"message": "ZeroDivisionError: Attempted to divide by zero.\n File \"...\""
}
}
```
## pyRevit Routes Core API
pyRevit Routes defines its own core API to help you test the status of the running instances of the Routes server on the target machine. Note that at least one instance of Revit must be running to be able to receive and respond to these calls.
↓ See this page for more information about the `routes/` and `pyrevit-core/` APIs
## Server Configurations
### Configuring Starting Port Number
Use the pyRevit CLI to configure the starting port number for the Routes servers running on the host machine. First Revit instance will start with this number and other instances will continue up adding one for each instance e.g. `48884` → `48885` → `48886` → ... See Runtime REST API for methods to know about running Routes servers on a machine.
```bash
# read port number
$ pyrevit configs routes port
Routes Port: 48884
# set port number
$ pyrevit configs routes port <port-number>
```
Each new Revit instance uses the next available port number.
## pyRevit Core API
See Runtime REST API for pyRevit Core API that provides methods to manage the pyRevit environment itself. This API can be toggled using the pyRevit Settings window or the pyRevit CLI:
```bash
# read core api status
$ pyrevit configs coreapi
Routes Core API is disabled
# set core api status
$ pyrevit configs coreapi enable
$ pyrevit configs coreapi disable
```
---
## **Current Implementation** |
A minimal and beginner-friendly implementation of the Model Context Protocol (MCP) for Autodesk Revit
- This implementation leverages the Routes module inside pyRevit to create a bridge between Revit and Large Language Models (LLMs).
- It provides a modular, extensible template for prototyping and iterating tools that give LLMs access to your Revit models.
- The tools are designed to be easily expanded for specific use cases. You're welcome to fork the repo and contribute.
- **Note:** The pyRevit Routes API is currently in draft form and subject to change. It lacks built-in authentication mechanisms, so you'll need to implement your own security measures for production use.
This repo is aimed at:
- Beginners to the Revit API
- Python specialists who aren't versed in C#
- Anyone wanting to prototype and iterate quickly with LLMs and Revit
It contains:
- A complete modular Routes implementation for pyRevit
- A FastMCP server script to connect to any MCP-compatible client
- Several categorized tools to get you started right away
- A modular architecture that makes adding new functionality straightforward
---
## **Available Tools** |
The current implementation provides these tools organized by category:
### **Status & Connectivity Tools**
- **get_revit_status**: Check if the Revit MCP API is active and responding
- **get_revit_model_info**: Get comprehensive information about the current Revit model
### **Model Information Tools**
- **list_levels**: Get all levels with elevation information in the current Revit model
### **View & Image Tools**
- **get_revit_view**: Export a specific Revit view as an image
- Takes view_name parameter
- Returns PNG image data
- Supports all exportable view types
- **list_revit_views**: Get a list of all exportable views in the current Revit model
- Organized by view type (floor plans, sections, 3D views, etc.)
### **Family & Placement Tools**
- **place_family**: Place a family instance at a specified location in the Revit model
- Parameters: family_name, type_name, x, y, z, rotation, level_name, properties
- Supports detailed placement with custom properties
- **list_families**: Get a flat list of available family types in the current Revit model
- Parameters: contains (filter), limit (max results)
- **list_family_categories**: Get a list of all family categories in the current Revit model
### **Code Execution Tools**
- **execute_revit_code**: Execute IronPython code directly in Revit context
- Parameters: code (string), description (optional)
- Provides access to doc, DB, revit modules
- Returns execution output and any print statements
- Handles transactions automatically for model modifications
- Use when existing MCP tools cannot accomplish the needed functionality
### **Color Splashing Tools**
- **splash_color**: Apply a color to all elements of a specific category in the Revit model
- Parameters: category_name, color (RGB tuple)
- Useful for visualizing categories in the model
- **clear_colors**: Remove all color overrides from elements of a specific category
- Parameters: category_name
- This tool may have performance implications for large models.
---
## **Current Route Endpoints** |
The pyRevit extension exposes these HTTP endpoints under `http://localhost:48884/revit_mcp/`:
### **Status Endpoints**
- `GET /status/` - Health check and API status
### **Model Information Endpoints**
- `GET /model_info/` - Comprehensive model information
- `GET /list_levels/` - Get all levels with elevation information
### **View Endpoints**
- `GET /get_view/<view_name>` - Export specific view as PNG image
- `GET /list_views/` - Get all exportable views organized by type
### **Placement Endpoints**
- `POST /place_family/` - Place family instance with detailed parameters
- `GET /list_families/` - Get available families and types (supports contains/limit params)
- `GET /list_family_categories/` - Get family categories with counts
### **Code Execution Endpoints**
- `POST /execute_code/` - Execute IronPython code in Revit context
- Parameters: code (string), description (optional)
- Handles transactions automatically for model modifications
- Use when existing MCP tools cannot accomplish the needed functionality
### **Color Splashing Endpoints**
- `POST /splash_color/` - Apply color to all elements of a specific category
- Parameters: category_name, color (RGB tuple)
---
## **Writing New Functions** |
The modular architecture makes it easy to add new functionality. The process involves three main parts:
1. **Create the Route in Revit:** Define the endpoint in a Python module that will run inside the pyRevit extension. This is the code that directly interacts with the Revit API.
2. **Create the MCP Tool:** Define the corresponding tool that the LLM will call. This tool lives on the MCP server and uses helper functions to call the Revit route.
3. **Register Your Modules:** Add your new route and tool modules to the registration files so the application loads them.
### **Part 1: Create the Route Module in Revit**
First, create a new Python file within the `revit-mcp-python.extension/revit_mcp/` directory (e.g., `revit_mcp/your_module.py`). This module will contain all the related functions you want to expose, whether they are for reading data (`GET`) or modifying the model (`POST`).
```
# In revit-mcp-python.extension/revit_mcp/your_module.py
# -*- coding: UTF-8 -*-
"""
Your Module for Revit MCP
Handles your specific functionality.
"""
from pyrevit import routes, revit, DB
import json
import logging
# Standard logger setup
logger = logging.getLogger(__name__)
def register_your_routes(api):
"""Register all your routes with the API."""
# ---- Example 1: A GET request for reading data ----
@api.route('/your_endpoint/', methods=["GET"])
def get_project_title(doc):
"""Gets the project title from the Revit model."""
try:
value = doc.Title
return routes.make_response(data={"status": "success", "data": value})
except Exception as e:
logger.error("Get project title failed: {}".format(str(e)))
return routes.make_response(data={"error": str(e)}, status=500)
# ---- Example 2: A POST request for modifying the model ----
@api.route('/modify_model/', methods=["POST"])
def modify_model(doc, request):
"""Handles POST requests for modifying the Revit model."""
try:
data = json.loads(request.data) if isinstance(request.data, str) else request.data
# Use a transaction for all model modifications
t = DB.Transaction(doc, "Modify Model via MCP")
t.Start()
try:
element_id = data.get("element_id")
new_value = data.get("new_value")
element = doc.GetElement(DB.ElementId(int(element_id)))
param = element.LookupParameter("Comments")
param.Set(new_value)
t.Commit()
return routes.make_response(data={"status": "success", "result": "Element modified."})
except Exception as tx_error:
if t.HasStarted() and not t.HasEnded():
t.RollBack()
raise tx_error
except Exception as e:
logger.error("Modify model failed: {}".format(str(e)))
return routes.make_response(data={"error": str(e)}, status=500)
logger.info("Your custom routes were registered successfully.")
```
### **Part 2: Create the MCP Tool Module**
Next, create the corresponding tools for the MCP server. This is what the LLM will actually see and use. Create a new file in the `tools/` directory (e.g., `tools/your_tools.py`). This module will use the `revit_get` and `revit_post` helpers from `main.py`.
```
# In tools/your_tools.py
# -*- coding: utf-8 -*-
"""Your tools for the MCP server."""
from mcp.server.fastmcp import Context
from .utils import format_response
def register_your_tools(mcp, revit_get, revit_post, revit_image=None):
"""Register your tools with the MCP server."""
# ---- Tool for the GET request ----
@mcp.tool()
async def get_revit_project_title(ctx: Context) -> str:
"""
Retrieves the title of the currently open Revit project.
"""
response = await revit_get("/your_endpoint/", ctx)
return format_response(response)
# ---- Tool for the POST request ----
@mcp.tool()
async def modify_revit_element_comment(
element_id: int,
new_value: str,
ctx: Context = None
) -> str:
"""
Modifies the 'Comments' parameter of a specific element.
Args:
element_id: The ID of the element to modify.
new_value: The new comment to apply to the element.
"""
payload = {"element_id": element_id, "new_value": new_value}
response = await revit_post("/modify_model/", payload, ctx)
return format_response(response)
```
### **Part 3: Register Your New Modules**
Finally, register both the route module and the tool module. This makes them active in the application. The registration process is the same whether your modules contain `GET`, `POST`, or a mix of function types.
**1. Register the Route Module:**
Open `revit-mcp-python.extension/startup.py` and add your new route registration function.
```
# In revit-mcp-python.extension/startup.py
# ... (other imports)
# Import the registration function from your new module
from revit_mcp.your_module import register_your_routes
def register_routes():
"""Register all MCP route modules"""
api = routes.API('revit_mcp')
try:
# ... (existing route registrations)
# Register your new routes (this registers all functions inside)
register_your_routes(api)
logger.info("All MCP routes registered successfully")
except Exception as e:
logger.error("Failed to register MCP routes: {}".format(str(e)))
raise
```
**2. Register the Tool Module:**
Open `tools/__init__.py` and add your new tool registration function.
```
# In tools/__init__.py
# ... (other tool imports)
# Import the registration function from your new tool module
from .your_tools import register_your_tools
def register_tools(mcp_server, revit_get_func, revit_post_func, revit_image_func):
"""Register all tools with the MCP server"""
# ... (existing tool registrations)
# Register your new tools (this registers all tools inside)
register_your_tools(mcp_server, revit_get_func, revit_post_func, revit_image_func)
```
---
## **IronPython Compatibility Notes** |
The pyRevit extension runs by default in IronPython, which has some specific compatibility requirements:
### **Important Syntax Rules**
- **No f-strings**: Use `.format()` instead of f-strings
- **Parameter Access**: Use parameter-based access for element properties to avoid `AttributeError: Name`
### **Safe Element Property Access**
Instead of direct property access like `element.Name`, use the utility functions from `revit_mcp.utils`:
```python
# ❌ This causes AttributeError: Name in IronPython
family_name = symbol.Family.Name
type_name = symbol.Name
# ✅ Use utility functions instead
from revit_mcp.utils import get_family_name_safe, get_element_name_safe
family_name = get_family_name_safe(symbol)
type_name = get_element_name_safe(symbol)
```
### **Available Utility Functions**
- `get_element_name_safe(element)` - Safely get element name
- `get_family_name_safe(family_symbol)` - Safely get family name
- `find_family_symbol_safely(doc, family_name, type_name)` - Find family symbols safely
### **String Formatting**
```python
# ❌ Don't use f-strings
message = f"Processing {count} elements"
# ✅ Use .format() instead
message = "Processing {} elements".format(count)
```
---
## **Extension Points** |
The modular architecture provides several extension points:
### **Adding New Route Categories**
1. Create new module in `revit_mcp/`
2. Register in `startup.py`
3. Create corresponding tool module in `tools/`
4. Register tools in `tools/__init__.py`
### **Extending Existing Categories**
- Add new routes to existing modules
- Add new tools to existing tool modules
- Both will be automatically registered
### **Custom Helper Functions**
- Add utility functions to `revit_mcp/utils.py` for Revit API operations
- Add response formatting utilities to `tools/utils.py` for MCP tools
- Import and use in any route or tool module
### **Advanced Error Handling**
- All modules include comprehensive error handling
- Use the established patterns for consistency
- Log errors appropriately for debugging
This modular approach ensures the codebase remains organized and maintainable while making it easy to add new functionality for specific use cases.