@rui.branco/revit-mcp 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PRIVACY.md +95 -0
- package/README.md +66 -43
- package/lib/tools/detail.js +2 -0
- package/lib/tools/diagnostics.js +2 -0
- package/lib/tools/document.js +5 -0
- package/lib/tools/documentation.js +10 -0
- package/lib/tools/geometry.js +4 -0
- package/lib/tools/graphics.js +5 -0
- package/lib/tools/install.js +2 -0
- package/lib/tools/material-appearance.js +2 -0
- package/lib/tools/materials.js +6 -0
- package/lib/tools/model.js +7 -0
- package/lib/tools/parameters.js +2 -0
- package/lib/tools/quality.js +5 -0
- package/lib/tools/read.js +6 -0
- package/lib/tools/reload.js +1 -0
- package/lib/tools/sheet-collections.js +2 -0
- package/lib/tools/sheets.js +3 -0
- package/lib/tools/titleblocks.js +2 -0
- package/lib/tools/views.js +18 -0
- package/lib/tools/write.js +4 -0
- package/manifest.json +5 -2
- package/package.json +3 -2
- package/revit-bridge/dist/RevitMcpBridge.Handlers.dll +0 -0
- package/revit-bridge/dist/RevitMcpBridge.dll +0 -0
package/PRIVACY.md
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Privacy Policy
|
|
2
|
+
|
|
3
|
+
**Effective date:** 21 September 2026
|
|
4
|
+
**Applies to:** the `revit-mcp` MCP server and its bundled Revit bridge add-in
|
|
5
|
+
(the "Software"), distributed as the npm package `@rui.branco/revit-mcp` and as
|
|
6
|
+
the Claude Desktop extension `revit-mcp.mcpb`.
|
|
7
|
+
|
|
8
|
+
## Summary
|
|
9
|
+
|
|
10
|
+
The Software runs entirely on your own computer. It has no backend, no account,
|
|
11
|
+
no telemetry and no analytics. It sends nothing to the author, and it does not
|
|
12
|
+
transmit your data anywhere over the internet.
|
|
13
|
+
|
|
14
|
+
## Data collection
|
|
15
|
+
|
|
16
|
+
**The author collects no data whatsoever.** The Software contains no telemetry,
|
|
17
|
+
crash reporting, usage analytics, update check or licence check. No personal
|
|
18
|
+
information, model content, file path or identifier is transmitted to the author
|
|
19
|
+
or to any third party operated by the author.
|
|
20
|
+
|
|
21
|
+
## What the Software processes, and where
|
|
22
|
+
|
|
23
|
+
The Software reads and edits the Revit model open on your machine. That data is
|
|
24
|
+
processed locally and in memory:
|
|
25
|
+
|
|
26
|
+
- The Node MCP server exchanges JSON with the bridge add-in strictly over
|
|
27
|
+
loopback HTTP at `http://127.0.0.1:48884`. The listener binds the `127.0.0.1`
|
|
28
|
+
literal, not a wildcard, so it is not reachable from your network.
|
|
29
|
+
- The bridge add-in runs inside `Revit.exe` and calls the Revit API directly.
|
|
30
|
+
- Model data leaves your machine only if your MCP client sends it onward. When
|
|
31
|
+
the client is Claude, your prompts and the tool results the model sees are
|
|
32
|
+
handled under **Anthropic's** privacy policy, not this one:
|
|
33
|
+
<https://www.anthropic.com/legal/privacy>
|
|
34
|
+
|
|
35
|
+
Choosing what to ask Claude, and therefore what model content is included in a
|
|
36
|
+
conversation, is yours to control.
|
|
37
|
+
|
|
38
|
+
## Local storage on your machine
|
|
39
|
+
|
|
40
|
+
The Software writes only these, all locally, and never transmits them:
|
|
41
|
+
|
|
42
|
+
| What | Where | Why |
|
|
43
|
+
| --- | --- | --- |
|
|
44
|
+
| Bridge log | `%LOCALAPPDATA%\RevitMcpBridge\bridge.log` | Diagnosing failures. May contain Revit element ids, view and sheet names, and error text. |
|
|
45
|
+
| Bridge add-in files | `%APPDATA%\Autodesk\Revit\Addins\<version>\` | The installed add-in and its `.addin` manifest. |
|
|
46
|
+
| Diagnostics buffer | In memory, inside Revit | The last 200 dismissed dialogs and resolved warnings. Lost when Revit closes. |
|
|
47
|
+
| Exports you request | The path you specify | Images and PDFs the Software writes only when a tool is called with that path. |
|
|
48
|
+
|
|
49
|
+
Delete the log file at any time; the Software recreates it only when it next
|
|
50
|
+
logs. Uninstalling the bridge (`revit_uninstall_bridge`) removes the add-in
|
|
51
|
+
files.
|
|
52
|
+
|
|
53
|
+
## Third-party sharing
|
|
54
|
+
|
|
55
|
+
**None.** The author does not share, sell, rent or disclose any data, because the
|
|
56
|
+
author never receives any.
|
|
57
|
+
|
|
58
|
+
The Software makes no outbound internet request of its own. Network activity you
|
|
59
|
+
may observe around it comes from other parties under their own policies: your MCP
|
|
60
|
+
client contacting its provider, and `npm`/`npx` downloading the package from
|
|
61
|
+
<https://registry.npmjs.org> at install time.
|
|
62
|
+
|
|
63
|
+
## Data retention
|
|
64
|
+
|
|
65
|
+
The author retains nothing, having received nothing. Local files listed above
|
|
66
|
+
stay on your machine until you delete them or uninstall the Software. The
|
|
67
|
+
in-memory diagnostics buffer holds at most 200 entries and is discarded when
|
|
68
|
+
Revit closes.
|
|
69
|
+
|
|
70
|
+
## Children
|
|
71
|
+
|
|
72
|
+
The Software is a professional tool for Autodesk Revit and is not directed at
|
|
73
|
+
children under 13.
|
|
74
|
+
|
|
75
|
+
## Security
|
|
76
|
+
|
|
77
|
+
The bridge add-in exposes an **unauthenticated** loopback HTTP listener with full
|
|
78
|
+
Revit API access while Revit is running: any local process on your machine can
|
|
79
|
+
reach it. The add-in is not code-signed. Review the
|
|
80
|
+
[Security](README.md#security) section of the README before installing on a
|
|
81
|
+
shared or untrusted machine.
|
|
82
|
+
|
|
83
|
+
## Changes to this policy
|
|
84
|
+
|
|
85
|
+
Material changes will be published in this file, with the effective date above
|
|
86
|
+
updated, and will appear in the repository's commit history:
|
|
87
|
+
<https://github.com/rui-branco/revit-mcp/commits/main/PRIVACY.md>
|
|
88
|
+
|
|
89
|
+
## Contact
|
|
90
|
+
|
|
91
|
+
Questions, or a privacy or security concern:
|
|
92
|
+
|
|
93
|
+
- Issues: <https://github.com/rui-branco/revit-mcp/issues>
|
|
94
|
+
- Private security reports:
|
|
95
|
+
<https://github.com/rui-branco/revit-mcp/security/advisories/new>
|
package/README.md
CHANGED
|
@@ -10,72 +10,77 @@
|
|
|
10
10
|
it, and produce a full drawing set in plain language — levels and elements,
|
|
11
11
|
sheets and schedules, views and PDF exports.
|
|
12
12
|
|
|
13
|
-
**Windows · Revit 2025, 2026 or 2027
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
Claude Code route fetches the package once via `npx`.
|
|
13
|
+
**Windows · Revit 2025, 2026 or 2027.** The add-in ships precompiled — nothing
|
|
14
|
+
to build, no .NET SDK. The Claude Desktop extension needs no Node.js either; the
|
|
15
|
+
Claude Code route uses `npx`, so it wants Node 18+.
|
|
17
16
|
|
|
18
17
|
## Installation
|
|
19
18
|
|
|
20
19
|
### 1. Register the server
|
|
21
20
|
|
|
22
|
-
|
|
21
|
+
Two commands, in **Claude Code or Claude Desktop** — same plugin, either app:
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
No JSON to edit and nothing to download at runtime: the extension carries the
|
|
29
|
-
server, its dependencies and the Revit add-in. Bridge URL and request timeout
|
|
30
|
-
are exposed as settings under **Settings → Extensions → Revit**.
|
|
23
|
+
```
|
|
24
|
+
/plugin marketplace add rui-branco/revit-mcp
|
|
25
|
+
/plugin install revit@revit-tools
|
|
26
|
+
```
|
|
31
27
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
That is the whole step. The plugin updates itself from this repo, so there is
|
|
29
|
+
nothing to re-download when a new version ships; `/plugin update` pulls it
|
|
30
|
+
early if you do not want to wait.
|
|
35
31
|
|
|
36
32
|
<details>
|
|
37
|
-
<summary>
|
|
33
|
+
<summary><b>Other ways to install</b></summary>
|
|
34
|
+
|
|
35
|
+
These exist for completeness. The plugin above is the supported path, and the
|
|
36
|
+
only one that updates on its own.
|
|
37
|
+
|
|
38
|
+
**Claude Code, without the plugin**
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
claude mcp add revit --scope user -- npx -y @rui.branco/revit-mcp
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`--scope user` enables it in every project; `--scope project` writes it to the
|
|
45
|
+
repo's `.mcp.json` instead, to share with a team.
|
|
46
|
+
|
|
47
|
+
**Claude Desktop, as a Desktop Extension (.mcpb)**
|
|
38
48
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
49
|
+
[Download `revit-mcp.mcpb`](https://github.com/rui-branco/revit-mcp/releases/latest/download/revit-mcp.mcpb),
|
|
50
|
+
then **Settings → Extensions → Advanced settings → Install extension…** and
|
|
51
|
+
pick the file. It bundles the server, its dependencies and the Revit add-in,
|
|
52
|
+
and runs on Claude Desktop's built-in Node runtime, so neither Node.js nor npm
|
|
53
|
+
is needed. **A sideloaded extension never auto-updates** — every new version
|
|
54
|
+
means downloading and installing the file again, which is why the plugin is
|
|
55
|
+
preferred.
|
|
56
|
+
|
|
57
|
+
**Claude Desktop, by hand**
|
|
58
|
+
|
|
59
|
+
**Settings → Developer → Edit Config** opens
|
|
60
|
+
`%APPDATA%\Claude\claude_desktop_config.json`. Add the `revit` entry, keeping
|
|
61
|
+
any servers already there:
|
|
42
62
|
|
|
43
63
|
```json
|
|
44
64
|
{
|
|
45
65
|
"mcpServers": {
|
|
46
66
|
"revit": {
|
|
47
67
|
"command": "npx",
|
|
48
|
-
"args": ["-y", "
|
|
68
|
+
"args": ["-y", "@rui.branco/revit-mcp"]
|
|
49
69
|
}
|
|
50
70
|
}
|
|
51
71
|
}
|
|
52
72
|
```
|
|
53
73
|
|
|
54
|
-
Then
|
|
55
|
-
enough — and reopen it.
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
`PATH`. Use absolute paths instead (`where.exe npx.cmd` prints yours), or point
|
|
59
|
-
`command` at your `node.exe` with `args` of
|
|
60
|
-
`["C:\\path\\to\\revit-mcp\\index.js"]`.
|
|
61
|
-
|
|
62
|
-
</details>
|
|
63
|
-
|
|
64
|
-
#### Claude Code — one command
|
|
65
|
-
|
|
66
|
-
```bash
|
|
67
|
-
claude mcp add revit --scope user -- npx -y github:rui-branco/revit-mcp
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
`--scope user` enables it in every project; `--scope project` writes it to the
|
|
71
|
-
repo's `.mcp.json` instead, to share with a team. Check it with `claude mcp list`.
|
|
74
|
+
Then quit Claude Desktop from the system tray — closing the window is not
|
|
75
|
+
enough — and reopen it. If the server shows as failed, Claude Desktop could not
|
|
76
|
+
find `npx` on its `PATH`: use an absolute path (`where.exe npx.cmd` prints
|
|
77
|
+
yours).
|
|
72
78
|
|
|
73
|
-
|
|
74
|
-
<summary><b>Other MCP clients</b></summary>
|
|
79
|
+
**Any other MCP client**
|
|
75
80
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
81
|
+
Run `npx -y @rui.branco/revit-mcp` as the server command. It takes no
|
|
82
|
+
arguments and needs no environment beyond the optional
|
|
83
|
+
[configuration](#configuration).
|
|
79
84
|
|
|
80
85
|
</details>
|
|
81
86
|
|
|
@@ -427,6 +432,24 @@ until the dialog is answered by hand.
|
|
|
427
432
|
| `did not answer within Nms` | Revit is busy or showing a modal dialog. | Check `revit_diagnostics` — a dialog seen but unanswered is recorded with `answered: false`. Otherwise clear the screen in Revit and retry, or raise `REVIT_MCP_TIMEOUT`. A timed-out write may still have completed. |
|
|
428
433
|
| `no active document` | Revit is on the start page or between documents. | Open a model and retry. `revit_status` deliberately tolerates this, which distinguishes it from the others. |
|
|
429
434
|
|
|
435
|
+
## Privacy Policy
|
|
436
|
+
|
|
437
|
+
**This software collects no data.** It has no backend, no account, no telemetry
|
|
438
|
+
and no analytics, and it makes no outbound internet request of its own. Model
|
|
439
|
+
data is processed locally, in memory, and over loopback only.
|
|
440
|
+
|
|
441
|
+
Model content leaves your machine only if your MCP client sends it onward —
|
|
442
|
+
when that client is Claude, under [Anthropic's privacy
|
|
443
|
+
policy](https://www.anthropic.com/legal/privacy), not this one.
|
|
444
|
+
|
|
445
|
+
Written locally and never transmitted: the bridge log at
|
|
446
|
+
`%LOCALAPPDATA%\RevitMcpBridge\bridge.log`, the add-in under
|
|
447
|
+
`%APPDATA%\Autodesk\Revit\Addins\`, an in-memory 200-entry diagnostics buffer,
|
|
448
|
+
and any image or PDF you ask a tool to export.
|
|
449
|
+
|
|
450
|
+
Full policy — collection, storage, third-party sharing, retention and contact:
|
|
451
|
+
**[PRIVACY.md](PRIVACY.md)**.
|
|
452
|
+
|
|
430
453
|
## Security
|
|
431
454
|
|
|
432
455
|
- The add-in runs **inside `Revit.exe` with full Revit API access**. It can
|
package/lib/tools/detail.js
CHANGED
|
@@ -37,6 +37,7 @@ export function registerDetailTools(server, bridge) {
|
|
|
37
37
|
.optional()
|
|
38
38
|
.describe("Line style name, e.g. 'Thin Lines'. Unknown names fall back to the default and are reported."),
|
|
39
39
|
},
|
|
40
|
+
{ title: "Draw Detail Lines", readOnlyHint: false, destructiveHint: false },
|
|
40
41
|
async ({ view_id, lines, line_style }) => {
|
|
41
42
|
try {
|
|
42
43
|
// The tool arguments stay snake_case like every other tool's; the bridge
|
|
@@ -74,6 +75,7 @@ export function registerDetailTools(server, bridge) {
|
|
|
74
75
|
.min(1)
|
|
75
76
|
.describe("Notes to place, all in this one call"),
|
|
76
77
|
},
|
|
78
|
+
{ title: "Add Text Notes", readOnlyHint: false, destructiveHint: false },
|
|
77
79
|
async ({ view_id, notes }) => {
|
|
78
80
|
try {
|
|
79
81
|
const result = await bridge.call("/detail/text", { viewId: view_id, notes });
|
package/lib/tools/diagnostics.js
CHANGED
|
@@ -16,6 +16,7 @@ export function registerDiagnosticsTools(server, bridge) {
|
|
|
16
16
|
"revit_diagnostics",
|
|
17
17
|
"Read what the bridge suppressed: the Revit dialogs it answered automatically and the transaction warnings it resolved, oldest first, plus whether auto-dismiss is currently on. CHECK THIS AFTER EVERY BATCH OF WRITES. A silently resolved warning usually means Revit changed something you did not ask for — walls joined differently, an element deleted as a side effect of another — and this buffer is the only record of it. An empty dialogs/failures list means the writes went through cleanly.",
|
|
18
18
|
{},
|
|
19
|
+
{ title: "Read Diagnostics", readOnlyHint: true },
|
|
19
20
|
async () => {
|
|
20
21
|
try {
|
|
21
22
|
const result = await bridge.call("/diagnostics");
|
|
@@ -40,6 +41,7 @@ export function registerDiagnosticsTools(server, bridge) {
|
|
|
40
41
|
.default(false)
|
|
41
42
|
.describe("Also empty the dialog/warning buffer that revit_diagnostics reads"),
|
|
42
43
|
},
|
|
44
|
+
{ title: "Set Dialog Auto-Dismiss", readOnlyHint: false, destructiveHint: false },
|
|
43
45
|
async ({ enabled, clear }) => {
|
|
44
46
|
try {
|
|
45
47
|
// The tool argument stays snake_case-free and plain, but the bridge
|
package/lib/tools/document.js
CHANGED
|
@@ -40,6 +40,7 @@ export function registerDocumentTools(server, bridge) {
|
|
|
40
40
|
"Rebuild the project in place: DELETES the existing save_path and the Revit backups beside it (House.0001.rvt, House.0002.rvt) and closes it in Revit first if it is open, then builds the project again at that same path. Defaults to false, which fails with FILE_EXISTS instead. A file something else still holds comes back as FILE_LOCKED — nothing is ever built under a different name.",
|
|
41
41
|
),
|
|
42
42
|
},
|
|
43
|
+
{ title: "New Project", readOnlyHint: false, destructiveHint: true },
|
|
43
44
|
async ({ save_path, template_path, overwrite }) => {
|
|
44
45
|
try {
|
|
45
46
|
// The tool arguments stay snake_case like every other tool's, but the
|
|
@@ -69,6 +70,7 @@ export function registerDocumentTools(server, bridge) {
|
|
|
69
70
|
"revit_save",
|
|
70
71
|
"Save the active document in place. A model that has never been saved has no path to save to and comes back as NOT_SAVEABLE — use revit_save_as for that one.",
|
|
71
72
|
{},
|
|
73
|
+
{ title: "Save Project", readOnlyHint: false, destructiveHint: false },
|
|
72
74
|
async () => {
|
|
73
75
|
try {
|
|
74
76
|
const result = await bridge.call("/document/save");
|
|
@@ -94,6 +96,7 @@ export function registerDocumentTools(server, bridge) {
|
|
|
94
96
|
.default(false)
|
|
95
97
|
.describe("Replace save_path if it already exists. Default false: an existing file fails with FILE_EXISTS."),
|
|
96
98
|
},
|
|
99
|
+
{ title: "Save Project As", readOnlyHint: false, destructiveHint: true },
|
|
97
100
|
async ({ save_path, overwrite }) => {
|
|
98
101
|
try {
|
|
99
102
|
// The tool arguments stay snake_case like every other tool's, but the
|
|
@@ -118,6 +121,7 @@ export function registerDocumentTools(server, bridge) {
|
|
|
118
121
|
.min(1)
|
|
119
122
|
.describe("Full path of the existing .rvt file to open, e.g. 'C:\\\\Projects\\\\House.rvt'"),
|
|
120
123
|
},
|
|
124
|
+
{ title: "Open Project", readOnlyHint: false, destructiveHint: false },
|
|
121
125
|
async ({ path }) => {
|
|
122
126
|
try {
|
|
123
127
|
const result = await bridge.call("/document/open", { path });
|
|
@@ -137,6 +141,7 @@ export function registerDocumentTools(server, bridge) {
|
|
|
137
141
|
.default(false)
|
|
138
142
|
.describe("Save the document before closing it. Default false: unsaved changes are discarded."),
|
|
139
143
|
},
|
|
144
|
+
{ title: "Close Project", readOnlyHint: false, destructiveHint: true },
|
|
140
145
|
async ({ save }) => {
|
|
141
146
|
try {
|
|
142
147
|
const result = await bridge.call("/document/close", { save });
|
|
@@ -36,6 +36,7 @@ export function registerDocumentationTools(server, bridge) {
|
|
|
36
36
|
.optional()
|
|
37
37
|
.describe("View ids to read, all in this one call. Use this or view_id, not both."),
|
|
38
38
|
},
|
|
39
|
+
{ title: "Read View Crop", readOnlyHint: true },
|
|
39
40
|
async ({ view_id, view_ids }) => {
|
|
40
41
|
// Checked here rather than in the schema: a raw shape cannot express
|
|
41
42
|
// "one of these two", and a call with neither must not reach Revit.
|
|
@@ -92,6 +93,7 @@ export function registerDocumentationTools(server, bridge) {
|
|
|
92
93
|
"DEFAULTS TO TRUE. True reports the current crop and the box that would be written, and changes nothing. Pass false to apply it.",
|
|
93
94
|
),
|
|
94
95
|
},
|
|
96
|
+
{ title: "Set View Crop", readOnlyHint: false, destructiveHint: false },
|
|
95
97
|
async ({ view_id, view_ids, model_bounds, dry_run }) => {
|
|
96
98
|
if (view_id === undefined && view_ids === undefined) {
|
|
97
99
|
return {
|
|
@@ -154,6 +156,7 @@ export function registerDocumentationTools(server, bridge) {
|
|
|
154
156
|
"DEFAULTS TO TRUE. True reports canBeHidden and the current isHidden for every id and changes nothing. Pass false to apply it.",
|
|
155
157
|
),
|
|
156
158
|
},
|
|
159
|
+
{ title: "Hide Elements in View", readOnlyHint: false, destructiveHint: false },
|
|
157
160
|
async ({ view_id, ids, hidden, dry_run }) => {
|
|
158
161
|
try {
|
|
159
162
|
const result = await bridge.call("/views/hide-elements", {
|
|
@@ -209,6 +212,7 @@ export function registerDocumentationTools(server, bridge) {
|
|
|
209
212
|
"False by default: an existing file at any target path fails the whole call with FILE_EXISTS and nothing is exported.",
|
|
210
213
|
),
|
|
211
214
|
},
|
|
215
|
+
{ title: "Export PDF", readOnlyHint: false, destructiveHint: false },
|
|
212
216
|
async ({ view_ids, sheet_ids, folder, filename, combine, overwrite }) => {
|
|
213
217
|
// Checked here rather than in the schema: a raw shape cannot express
|
|
214
218
|
// "at least one of these two", and an empty export must not reach Revit.
|
|
@@ -262,6 +266,7 @@ export function registerDocumentationTools(server, bridge) {
|
|
|
262
266
|
.default(0)
|
|
263
267
|
.describe("Body rows to skip, for paging through a long schedule"),
|
|
264
268
|
},
|
|
269
|
+
{ title: "Read Schedule Contents", readOnlyHint: true },
|
|
265
270
|
async ({ schedule_id, limit, offset }) => {
|
|
266
271
|
try {
|
|
267
272
|
const result = await bridge.call("/schedules/read", {
|
|
@@ -282,6 +287,7 @@ export function registerDocumentationTools(server, bridge) {
|
|
|
282
287
|
{
|
|
283
288
|
sheet_id: z.number().int().describe("Sheet id from revit_list_sheets"),
|
|
284
289
|
},
|
|
290
|
+
{ title: "Measure Sheet Layout", readOnlyHint: true },
|
|
285
291
|
async ({ sheet_id }) => {
|
|
286
292
|
try {
|
|
287
293
|
const result = await bridge.call("/sheets/layout", { sheetId: sheet_id });
|
|
@@ -310,6 +316,7 @@ export function registerDocumentationTools(server, bridge) {
|
|
|
310
316
|
`Max sheets to return (default ${DEFAULT_QUERY_LIMIT}, hard cap ${MAX_QUERY_LIMIT} — higher values are clamped, not rejected)`,
|
|
311
317
|
),
|
|
312
318
|
},
|
|
319
|
+
{ title: "Read Browser Organization", readOnlyHint: true },
|
|
313
320
|
async ({ sheet_ids, limit }) => {
|
|
314
321
|
try {
|
|
315
322
|
const result = await bridge.call("/sheets/browser-organization", {
|
|
@@ -353,6 +360,7 @@ export function registerDocumentationTools(server, bridge) {
|
|
|
353
360
|
"DEFAULTS TO TRUE. True reports the current position and moves nothing. Pass false to apply it.",
|
|
354
361
|
),
|
|
355
362
|
},
|
|
363
|
+
{ title: "Move Viewport on Sheet", readOnlyHint: false, destructiveHint: false },
|
|
356
364
|
async ({ viewport_id, center, label_offset, label_line_length, dry_run }) => {
|
|
357
365
|
try {
|
|
358
366
|
const result = await bridge.call("/sheets/set-viewport-position", {
|
|
@@ -389,6 +397,7 @@ export function registerDocumentationTools(server, bridge) {
|
|
|
389
397
|
"DEFAULTS TO TRUE. True reports the current position and moves nothing. Pass false to apply it.",
|
|
390
398
|
),
|
|
391
399
|
},
|
|
400
|
+
{ title: "Move Schedule on Sheet", readOnlyHint: false, destructiveHint: false },
|
|
392
401
|
async ({ instance_id, top_left, dry_run }) => {
|
|
393
402
|
try {
|
|
394
403
|
const result = await bridge.call("/sheets/set-schedule-position", {
|
|
@@ -489,6 +498,7 @@ export function registerDocumentationTools(server, bridge) {
|
|
|
489
498
|
"DEFAULTS TO TRUE. True reports the current configuration and changes nothing. Pass false to apply it.",
|
|
490
499
|
),
|
|
491
500
|
},
|
|
501
|
+
{ title: "Configure Schedule", readOnlyHint: false, destructiveHint: false },
|
|
492
502
|
async ({ schedule_id, itemized, group_by, fields, filters, grand_total, dry_run }) => {
|
|
493
503
|
if (
|
|
494
504
|
itemized === undefined &&
|
package/lib/tools/geometry.js
CHANGED
|
@@ -169,6 +169,7 @@ export function registerGeometryTools(server, bridge) {
|
|
|
169
169
|
.min(1)
|
|
170
170
|
.describe("One element per entry, all in this one call"),
|
|
171
171
|
},
|
|
172
|
+
{ title: "Create Geometry Elements", readOnlyHint: false, destructiveHint: false },
|
|
172
173
|
async ({ category, name, type_name, material_id, material_name, comments, mark, shapes }) => {
|
|
173
174
|
try {
|
|
174
175
|
const result = await bridge.call("/directshape/create", {
|
|
@@ -208,6 +209,7 @@ export function registerGeometryTools(server, bridge) {
|
|
|
208
209
|
.describe("Comments applied to every tree that does not carry its own"),
|
|
209
210
|
mark: z.string().min(1).optional().describe("Mark applied to every tree that does not carry its own"),
|
|
210
211
|
},
|
|
212
|
+
{ title: "Plant Trees", readOnlyHint: false, destructiveHint: false },
|
|
211
213
|
async ({
|
|
212
214
|
points,
|
|
213
215
|
trunk_height,
|
|
@@ -271,6 +273,7 @@ export function registerGeometryTools(server, bridge) {
|
|
|
271
273
|
.describe("Comments applied to every run that does not carry its own"),
|
|
272
274
|
mark: z.string().min(1).optional().describe("Mark applied to every run that does not carry its own"),
|
|
273
275
|
},
|
|
276
|
+
{ title: "Create Pipe Runs", readOnlyHint: false, destructiveHint: false },
|
|
274
277
|
async ({ runs, category, name, type_name, material_id, material_name, comments, mark }) => {
|
|
275
278
|
try {
|
|
276
279
|
const result = await bridge.call("/pipes/create", {
|
|
@@ -309,6 +312,7 @@ export function registerGeometryTools(server, bridge) {
|
|
|
309
312
|
.describe("Comments applied to every head that does not carry its own"),
|
|
310
313
|
mark: z.string().min(1).optional().describe("Mark applied to every head that does not carry its own"),
|
|
311
314
|
},
|
|
315
|
+
{ title: "Place Sprinklers", readOnlyHint: false, destructiveHint: false },
|
|
312
316
|
async ({ points, radius, height, name, type_name, material_id, material_name, comments, mark }) => {
|
|
313
317
|
try {
|
|
314
318
|
const result = await bridge.call("/sprinklers/place", {
|
package/lib/tools/graphics.js
CHANGED
|
@@ -23,6 +23,7 @@ export function registerGraphicsTools(server, bridge) {
|
|
|
23
23
|
.optional()
|
|
24
24
|
.describe("Ids of several views to read in one call. Use this or view_id, not both."),
|
|
25
25
|
},
|
|
26
|
+
{ title: "Read View Graphics", readOnlyHint: true },
|
|
26
27
|
async ({ view_id, view_ids }) => {
|
|
27
28
|
try {
|
|
28
29
|
if (view_id === undefined && view_ids === undefined) {
|
|
@@ -82,6 +83,7 @@ export function registerGraphicsTools(server, bridge) {
|
|
|
82
83
|
"Turn cast shadows on or off. The bridge probes the view first and reports whether it wrote the parameter (verified) or posted Revit's own command (pending and unverified).",
|
|
83
84
|
),
|
|
84
85
|
},
|
|
86
|
+
{ title: "Set View Graphics", readOnlyHint: false, destructiveHint: false },
|
|
85
87
|
async ({ view_id, view_ids, style, detail_level, shadow_intensity, sunlight_intensity, shadows }) => {
|
|
86
88
|
try {
|
|
87
89
|
if (view_id === undefined && view_ids === undefined) {
|
|
@@ -107,6 +109,7 @@ export function registerGraphicsTools(server, bridge) {
|
|
|
107
109
|
"revit_get_view_graphics_command_status",
|
|
108
110
|
"What became of the last cast-shadows command revit_set_view_graphics posted: when it was posted, whether Revit has been idle since (which is when a posted command actually runs), and a fresh probe of the shadows parameter now, next to the one taken at the moment of posting. 'verified' is true ONLY when the parameter can be read back as an integer and matches what was asked for; when it cannot be read, verified stays false and verifiedBy is null — a posted UI command that nothing can observe is reported as unverified, not as success. Call this after a set-graphics response that said method 'posted-command'.",
|
|
109
111
|
{},
|
|
112
|
+
{ title: "View Graphics Command Status", readOnlyHint: true },
|
|
110
113
|
async () => {
|
|
111
114
|
try {
|
|
112
115
|
const result = await bridge.call("/views/graphics-command-status");
|
|
@@ -140,6 +143,7 @@ export function registerGraphicsTools(server, bridge) {
|
|
|
140
143
|
"Exact parameter ids the template should control, from a previous capture's response. Not to be combined with mode.",
|
|
141
144
|
),
|
|
142
145
|
},
|
|
146
|
+
{ title: "Capture View Template", readOnlyHint: false, destructiveHint: false },
|
|
143
147
|
async ({ source_view_id, name, mode, parameter_ids }) => {
|
|
144
148
|
try {
|
|
145
149
|
const result = await bridge.call("/views/capture-template", {
|
|
@@ -179,6 +183,7 @@ export function registerGraphicsTools(server, bridge) {
|
|
|
179
183
|
.optional()
|
|
180
184
|
.describe("Allow 'assign' to detach a template a view already has. Without it, such a view fails the call instead.",),
|
|
181
185
|
},
|
|
186
|
+
{ title: "Apply View Template", readOnlyHint: false, destructiveHint: false },
|
|
182
187
|
async ({ template_id, view_ids, mode, dry_run, replace }) => {
|
|
183
188
|
try {
|
|
184
189
|
const result = await bridge.call("/views/apply-template", {
|
package/lib/tools/install.js
CHANGED
|
@@ -179,6 +179,7 @@ export function registerInstallTools(server, options = {}) {
|
|
|
179
179
|
.optional()
|
|
180
180
|
.describe("Never build, even in a source checkout with no prebuilt add-in. A no-op for the published package, which always installs its bundled binary without building."),
|
|
181
181
|
},
|
|
182
|
+
{ title: "Install Bridge Add-In", readOnlyHint: false, destructiveHint: false },
|
|
182
183
|
async ({ revit_version, skip_build }) => {
|
|
183
184
|
try {
|
|
184
185
|
const result = await runInstaller(
|
|
@@ -196,6 +197,7 @@ export function registerInstallTools(server, options = {}) {
|
|
|
196
197
|
"revit_uninstall_bridge",
|
|
197
198
|
"Remove the Revit MCP bridge add-in: deletes the .addin manifest and the install folder for every Revit version that has it. Revit does NOT need to be running, but RESTART REVIT afterwards — a running Revit keeps the already-loaded bridge alive until it closes.",
|
|
198
199
|
{},
|
|
200
|
+
{ title: "Uninstall Bridge Add-In", readOnlyHint: false, destructiveHint: true },
|
|
199
201
|
async () => {
|
|
200
202
|
try {
|
|
201
203
|
const result = await runInstaller({ uninstall: true }, options);
|
|
@@ -53,6 +53,7 @@ export function registerMaterialAppearanceTools(server, bridge) {
|
|
|
53
53
|
"revit_get_material_appearance",
|
|
54
54
|
"Read the appearance asset behind a material — the rendered look, as opposed to the shading colour revit_create_material sets. Read-only: it changes nothing. It reports the material's shading side (colorRgb, transparency, shininess, smoothness, useRenderAppearanceForShading), the AppearanceAssetElement it really points at (id, name and the SCHEMA it was built from, e.g. 'Generic' or 'Ceramic'), and every direct property of that asset: 'name' as the API knows it (e.g. 'generic_diffuse'), 'type' and 'runtimeType', the typed 'value' it holds, and 'patchType' — which of revit_set_material_appearance's five types can write it, or null when none can. Anything connected to a property comes back under 'connected' with the bitmap's file, tile size in feet and rotation. 'sharedWithMaterialIds' is every OTHER material pointing at the same asset: non-empty means editing it in place would repaint them too, which is exactly what revit_set_material_appearance refuses to do. NOTE on 'readOnly': Revit hands the rendering asset out read-only outside an edit scope, so it is usually true for every property and is NOT the test of whether a property can be written — revit_set_material_appearance is, because it validates inside an edit scope. A material with no appearance asset at all is a normal state, not a broken one: it renders from Color and Transparency alone, appearanceAssetId comes back null with an empty property list, and it does NOT need a bitmap — create_generic on revit_set_material_appearance gives it a textureless Generic asset to patch. 'genericAssetAvailable' says up front whether Revit's library can supply one on this machine.",
|
|
55
55
|
MATERIAL,
|
|
56
|
+
{ title: "Read Material Appearance", readOnlyHint: true },
|
|
56
57
|
async ({ material_id, material_name }) => {
|
|
57
58
|
try {
|
|
58
59
|
const result = await bridge.call("/materials/appearance", {
|
|
@@ -131,6 +132,7 @@ export function registerMaterialAppearanceTools(server, bridge) {
|
|
|
131
132
|
"Also write the first patched colour onto the material's own Color, which is what shaded views draw. Needs at least one 'color' patch.",
|
|
132
133
|
),
|
|
133
134
|
},
|
|
135
|
+
{ title: "Edit Material Appearance", readOnlyHint: false, destructiveHint: false },
|
|
134
136
|
async ({
|
|
135
137
|
material_id,
|
|
136
138
|
material_name,
|
package/lib/tools/materials.js
CHANGED
|
@@ -58,6 +58,7 @@ export function registerMaterialTools(server, bridge) {
|
|
|
58
58
|
"revit_list_materials",
|
|
59
59
|
"List the materials in the document: id, name, colour as {r, g, b}, and the id of its appearance asset when it has one. This is where a material_id comes from — for revit_create_directshape, revit_place_planting, revit_create_pipes, revit_place_sprinklers, revit_assign_material, revit_create_wall_type and revit_create_floor_type. A Revit template usually ships dozens, so check here before creating a new one.",
|
|
60
60
|
{},
|
|
61
|
+
{ title: "List Materials", readOnlyHint: true },
|
|
61
62
|
async () => {
|
|
62
63
|
try {
|
|
63
64
|
const result = await bridge.call("/materials", {});
|
|
@@ -110,6 +111,7 @@ export function registerMaterialTools(server, bridge) {
|
|
|
110
111
|
"Full path to a texture bitmap, to make the material textured in this same call. Revit's own library is under C:\\Program Files\\Common Files\\Autodesk Shared\\Materials\\Textures. A file that does not exist is refused before anything is created. Tile size, rotation and tint belong to revit_set_material_texture.",
|
|
111
112
|
),
|
|
112
113
|
},
|
|
114
|
+
{ title: "Create Material", readOnlyHint: false, destructiveHint: false },
|
|
113
115
|
async ({
|
|
114
116
|
name,
|
|
115
117
|
color,
|
|
@@ -168,6 +170,7 @@ export function registerMaterialTools(server, bridge) {
|
|
|
168
170
|
.optional()
|
|
169
171
|
.describe("Colour multiplied over the bitmap, as {r, g, b}, each channel 0-255"),
|
|
170
172
|
},
|
|
173
|
+
{ title: "Set Material Texture", readOnlyHint: false, destructiveHint: false },
|
|
171
174
|
async ({ material_id, material_name, texture_path, scale, rotation, tint }) => {
|
|
172
175
|
try {
|
|
173
176
|
const result = await bridge.call("/materials/set-texture", {
|
|
@@ -192,6 +195,7 @@ export function registerMaterialTools(server, bridge) {
|
|
|
192
195
|
material_id: z.number().int().describe("Material id from revit_list_materials or revit_create_material"),
|
|
193
196
|
element_ids: z.array(z.number().int()).min(1).describe("Elements to put the material on"),
|
|
194
197
|
},
|
|
198
|
+
{ title: "Assign Material", readOnlyHint: false, destructiveHint: false },
|
|
195
199
|
async ({ material_id, element_ids }) => {
|
|
196
200
|
try {
|
|
197
201
|
const result = await bridge.call("/materials/assign", {
|
|
@@ -209,6 +213,7 @@ export function registerMaterialTools(server, bridge) {
|
|
|
209
213
|
"revit_create_wall_type",
|
|
210
214
|
"Create a wall type carrying a material and a thickness, by duplicating an existing type and giving it a single structural layer. A wall's material is a property of its type, not of the wall, so this is the only way to get walls that are not the template's default grey: create the type, then pass its name to revit_create_walls as wall_type. A type that already exists by this name is REUSED and comes back with created:false and its current thickness and material — it is not re-cut to match the request. Thickness is feet (Revit internal units). One call is one undo step.",
|
|
211
215
|
HOST_TYPE,
|
|
216
|
+
{ title: "Create Wall Type", readOnlyHint: false, destructiveHint: false },
|
|
212
217
|
async ({ name, based_on_type_name, thickness, material_id, material_name }) => {
|
|
213
218
|
try {
|
|
214
219
|
const result = await bridge.call("/walltypes/create", {
|
|
@@ -229,6 +234,7 @@ export function registerMaterialTools(server, bridge) {
|
|
|
229
234
|
"revit_create_floor_type",
|
|
230
235
|
"Create a floor type carrying a material and a thickness, by duplicating an existing type and giving it a single structural layer. Like walls, a floor's material lives on its type — this is how paving stops being grey: create the type, then pass its name to revit_create_floor as type_name. A type that already exists by this name is REUSED and comes back with created:false and its current thickness and material. Thickness is feet (Revit internal units). One call is one undo step.",
|
|
231
236
|
HOST_TYPE,
|
|
237
|
+
{ title: "Create Floor Type", readOnlyHint: false, destructiveHint: false },
|
|
232
238
|
async ({ name, based_on_type_name, thickness, material_id, material_name }) => {
|
|
233
239
|
try {
|
|
234
240
|
const result = await bridge.call("/floortypes/create", {
|
package/lib/tools/model.js
CHANGED
|
@@ -43,6 +43,7 @@ export function registerModelTools(server, bridge) {
|
|
|
43
43
|
.optional()
|
|
44
44
|
.describe("Level the toposolid is hosted on. Omit it to use the lowest level in the document."),
|
|
45
45
|
},
|
|
46
|
+
{ title: "Create Site Surface", readOnlyHint: false, destructiveHint: false },
|
|
46
47
|
async ({ points, type_name, level }) => {
|
|
47
48
|
try {
|
|
48
49
|
const result = await bridge.call("/toposolid/create", {
|
|
@@ -72,6 +73,7 @@ export function registerModelTools(server, bridge) {
|
|
|
72
73
|
.optional()
|
|
73
74
|
.describe("Id of the toposolid to flatten. Only needed when the document has more than one."),
|
|
74
75
|
},
|
|
76
|
+
{ title: "Flatten Site Surface Region", readOnlyHint: false, destructiveHint: true },
|
|
75
77
|
async ({ points, elevation, toposolid_id }) => {
|
|
76
78
|
try {
|
|
77
79
|
const result = await bridge.call("/toposolid/flatten", {
|
|
@@ -111,6 +113,7 @@ export function registerModelTools(server, bridge) {
|
|
|
111
113
|
"Height offset from the level in feet, positive up. Defaults to 0. Written to the floor's 'Height Offset From Level' parameter and read back into the response.",
|
|
112
114
|
),
|
|
113
115
|
},
|
|
116
|
+
{ title: "Create Floor", readOnlyHint: false, destructiveHint: false },
|
|
114
117
|
async ({ level, boundary, type_name, structural, offset }) => {
|
|
115
118
|
try {
|
|
116
119
|
const result = await bridge.call("/floors/create", {
|
|
@@ -138,6 +141,7 @@ export function registerModelTools(server, bridge) {
|
|
|
138
141
|
.describe("Full paths of .rfa files to load. Pass the whole batch in one call."),
|
|
139
142
|
path: z.string().min(1).optional().describe("A single .rfa path, as a shorthand for paths"),
|
|
140
143
|
},
|
|
144
|
+
{ title: "Load Families", readOnlyHint: false, destructiveHint: false },
|
|
141
145
|
async ({ paths, path }) => {
|
|
142
146
|
try {
|
|
143
147
|
if (!paths && !path) {
|
|
@@ -172,6 +176,7 @@ export function registerModelTools(server, bridge) {
|
|
|
172
176
|
"Family name to filter by, e.g. 'M_RPC Tree - Deciduous'. This is the family, not the type — it returns every type in that family.",
|
|
173
177
|
),
|
|
174
178
|
},
|
|
179
|
+
{ title: "List Family Types", readOnlyHint: true },
|
|
175
180
|
async ({ category, family_name }) => {
|
|
176
181
|
try {
|
|
177
182
|
const result = await bridge.call("/families/symbols", {
|
|
@@ -215,6 +220,7 @@ export function registerModelTools(server, bridge) {
|
|
|
215
220
|
"Rotation in radians about the vertical axis through each point, counter-clockwise in plan. Radians is Revit's internal angle unit, as feet is its internal length unit.",
|
|
216
221
|
),
|
|
217
222
|
},
|
|
223
|
+
{ title: "Place Families", readOnlyHint: false, destructiveHint: false },
|
|
218
224
|
async ({ symbol_id, level, points, z: zOffset, rotation }) => {
|
|
219
225
|
try {
|
|
220
226
|
const result = await bridge.call("/families/place", {
|
|
@@ -264,6 +270,7 @@ export function registerModelTools(server, bridge) {
|
|
|
264
270
|
"Height of the sill above the level, in feet. Omit it for 3 feet on a window and Revit's own on anything else.",
|
|
265
271
|
),
|
|
266
272
|
},
|
|
273
|
+
{ title: "Place Doors and Windows", readOnlyHint: false, destructiveHint: false },
|
|
267
274
|
async ({ symbol_id, points, host_wall_id, level, sill_height }) => {
|
|
268
275
|
try {
|
|
269
276
|
// The tool arguments stay snake_case like every other tool's; the bridge
|
package/lib/tools/parameters.js
CHANGED
|
@@ -63,6 +63,7 @@ export function registerParameterTools(server, bridge) {
|
|
|
63
63
|
.optional()
|
|
64
64
|
.describe("True (the default) binds it per element; false binds it to the type, so every element of that type shares one value."),
|
|
65
65
|
},
|
|
66
|
+
{ title: "Create Project Parameter", readOnlyHint: false, destructiveHint: false },
|
|
66
67
|
async ({ name, category, categories, type, group, instance }) => {
|
|
67
68
|
// Checked here rather than in the schema: a raw shape cannot express
|
|
68
69
|
// "one of these two", and a call with neither must not reach Revit.
|
|
@@ -120,6 +121,7 @@ export function registerParameterTools(server, bridge) {
|
|
|
120
121
|
.min(1)
|
|
121
122
|
.describe("Sheet/name/value triples, all in this one call"),
|
|
122
123
|
},
|
|
124
|
+
{ title: "Set Sheet Parameters", readOnlyHint: false, destructiveHint: false },
|
|
123
125
|
async ({ values }) => {
|
|
124
126
|
try {
|
|
125
127
|
// The tool arguments stay snake_case like every other tool's; the bridge
|
package/lib/tools/quality.js
CHANGED
|
@@ -50,6 +50,7 @@ export function registerQualityTools(server, bridge) {
|
|
|
50
50
|
"Force vertex/segment lists on or off. Omit it and they come back whenever there are 500 or fewer, and collapse to a count plus a min/max Z above that. true asks for them anyway, capped at 500 with truncated: true.",
|
|
51
51
|
),
|
|
52
52
|
},
|
|
53
|
+
{ title: "Inspect Elements", readOnlyHint: true },
|
|
53
54
|
async ({ ids, include_parameters, include_geometry }) => {
|
|
54
55
|
try {
|
|
55
56
|
// The tool arguments stay snake_case like every other tool's, but the
|
|
@@ -88,6 +89,7 @@ export function registerQualityTools(server, bridge) {
|
|
|
88
89
|
"true (the default) measures and reports what the move would do without changing the model. Pass false to actually move.",
|
|
89
90
|
),
|
|
90
91
|
},
|
|
92
|
+
{ title: "Move Elements", readOnlyHint: false, destructiveHint: false },
|
|
91
93
|
async ({ ids, translation, dry_run }) => {
|
|
92
94
|
try {
|
|
93
95
|
const result = await bridge.call("/elements/move", {
|
|
@@ -129,6 +131,7 @@ export function registerQualityTools(server, bridge) {
|
|
|
129
131
|
"true (the default) runs the CanBeExcavatedBy preflight and reports the current volume without changing the model. Pass false to actually excavate.",
|
|
130
132
|
),
|
|
131
133
|
},
|
|
134
|
+
{ title: "Excavate Site Surface", readOnlyHint: false, destructiveHint: false },
|
|
132
135
|
async ({ toposolid_id, ids, dry_run }) => {
|
|
133
136
|
try {
|
|
134
137
|
const result = await bridge.call("/toposolid/excavate", {
|
|
@@ -165,6 +168,7 @@ export function registerQualityTools(server, bridge) {
|
|
|
165
168
|
.default(0)
|
|
166
169
|
.describe("Warnings to skip, for paging through a noisy model"),
|
|
167
170
|
},
|
|
171
|
+
{ title: "Read Model Warnings", readOnlyHint: true },
|
|
168
172
|
async ({ limit, offset }) => {
|
|
169
173
|
try {
|
|
170
174
|
const result = await bridge.call("/document/warnings", {
|
|
@@ -185,6 +189,7 @@ export function registerQualityTools(server, bridge) {
|
|
|
185
189
|
"revit_list_view_templates",
|
|
186
190
|
"List the view templates in the model: id, name, view type, and the parameters each template CONTROLS with their labels. Read-only. Check this before setting a scale, a display style or a category override on a view — a parameter the template controls is one the view cannot hold its own value for, which is why a setting you wrote reads back as something else.",
|
|
187
191
|
{},
|
|
192
|
+
{ title: "List View Templates", readOnlyHint: true },
|
|
188
193
|
async () => {
|
|
189
194
|
try {
|
|
190
195
|
const result = await bridge.call("/views/templates");
|
package/lib/tools/read.js
CHANGED
|
@@ -19,6 +19,7 @@ export function registerReadTools(server, bridge) {
|
|
|
19
19
|
"revit_status",
|
|
20
20
|
"Check whether Revit is reachable: version, active document name/path, and whether the model is workshared. Run this first when any other Revit tool fails.",
|
|
21
21
|
{},
|
|
22
|
+
{ title: "Revit Status", readOnlyHint: true },
|
|
22
23
|
async () => {
|
|
23
24
|
try {
|
|
24
25
|
const result = await bridge.call("/status");
|
|
@@ -33,6 +34,7 @@ export function registerReadTools(server, bridge) {
|
|
|
33
34
|
"revit_list_levels",
|
|
34
35
|
"List the levels in the active document: id, name and elevation (Revit internal units, decimal feet).",
|
|
35
36
|
{},
|
|
37
|
+
{ title: "List Levels", readOnlyHint: true },
|
|
36
38
|
async () => {
|
|
37
39
|
try {
|
|
38
40
|
const result = await bridge.call("/levels");
|
|
@@ -47,6 +49,7 @@ export function registerReadTools(server, bridge) {
|
|
|
47
49
|
"revit_list_categories",
|
|
48
50
|
"List the categories present in the active document with an element count each. Use this to find the exact category name to pass to revit_query_elements.",
|
|
49
51
|
{},
|
|
52
|
+
{ title: "List Categories", readOnlyHint: true },
|
|
50
53
|
async () => {
|
|
51
54
|
try {
|
|
52
55
|
const result = await bridge.call("/categories");
|
|
@@ -85,6 +88,7 @@ export function registerReadTools(server, bridge) {
|
|
|
85
88
|
.default(0)
|
|
86
89
|
.describe("Rows to skip, for paging through a large result"),
|
|
87
90
|
},
|
|
91
|
+
{ title: "Find Elements", readOnlyHint: true },
|
|
88
92
|
async ({ category, level, type_name, limit, offset }) => {
|
|
89
93
|
try {
|
|
90
94
|
// The tool argument stays snake_case like every other tool's, but the
|
|
@@ -116,6 +120,7 @@ export function registerReadTools(server, bridge) {
|
|
|
116
120
|
.optional()
|
|
117
121
|
.describe("Parameter names to read, e.g. ['Comments', 'Mark', 'Unconnected Height']"),
|
|
118
122
|
},
|
|
123
|
+
{ title: "Read Elements", readOnlyHint: true },
|
|
119
124
|
async ({ ids, params }) => {
|
|
120
125
|
try {
|
|
121
126
|
const result = await bridge.call("/elements", { ids, params });
|
|
@@ -130,6 +135,7 @@ export function registerReadTools(server, bridge) {
|
|
|
130
135
|
"revit_get_selection",
|
|
131
136
|
"Read what the user currently has selected in the Revit UI. Use this when the user says 'this wall', 'the selected elements' or similar.",
|
|
132
137
|
{},
|
|
138
|
+
{ title: "Read Current Selection", readOnlyHint: true },
|
|
133
139
|
async () => {
|
|
134
140
|
try {
|
|
135
141
|
const result = await bridge.call("/selection");
|
package/lib/tools/reload.js
CHANGED
|
@@ -15,6 +15,7 @@ export function registerReloadTools(server, bridge) {
|
|
|
15
15
|
"revit_reload_bridge",
|
|
16
16
|
"Reload the bridge's endpoint logic from disk without restarting Revit, after rebuilding the add-in. Use this while developing the bridge itself: it swaps the freshly built logic assembly into the running Revit, keeping the open model, the HTTP listener and any queued work alive. The response says whether the previous version was actually unloaded — if unloadedPrevious is false, the new logic IS live but the old one is still in memory, which is worth investigating rather than ignoring. Nothing changes until the DLL on disk changes, so build first.",
|
|
17
17
|
{},
|
|
18
|
+
{ title: "Reload Bridge Logic", readOnlyHint: false, destructiveHint: false },
|
|
18
19
|
async () => {
|
|
19
20
|
try {
|
|
20
21
|
const result = await bridge.call("/reload");
|
|
@@ -10,6 +10,7 @@ export function registerSheetCollectionTools(server, bridge) {
|
|
|
10
10
|
"revit_list_sheet_collections",
|
|
11
11
|
"List the sheet collections in the active document — the collapsible groups Revit draws under Sheets in the Project Browser — with each collection's id, name and member sheets (id, number, name). 'unassignedSheets' is every sheet in no collection. Read-only.",
|
|
12
12
|
{},
|
|
13
|
+
{ title: "List Sheet Collections", readOnlyHint: true },
|
|
13
14
|
async () => {
|
|
14
15
|
try {
|
|
15
16
|
const result = await bridge.call("/sheets/collections");
|
|
@@ -46,6 +47,7 @@ export function registerSheetCollectionTools(server, bridge) {
|
|
|
46
47
|
.optional()
|
|
47
48
|
.describe("Defaults to true: report the plan without writing. Pass false to apply it."),
|
|
48
49
|
},
|
|
50
|
+
{ title: "Group Sheets into Collections", readOnlyHint: false, destructiveHint: false },
|
|
49
51
|
async ({ collections, dry_run }) => {
|
|
50
52
|
try {
|
|
51
53
|
// Tool arguments stay snake_case; the bridge reads sheetIds / dryRun.
|
package/lib/tools/sheets.js
CHANGED
|
@@ -11,6 +11,7 @@ export function registerSheetTools(server, bridge) {
|
|
|
11
11
|
"revit_list_titleblocks",
|
|
12
12
|
"List the title block family types loaded in the active document: id, family name and type name. Call this before revit_create_sheets — every sheet needs a title block, and this is where its id comes from. An empty list means no title block family is loaded, so sheets cannot be created yet.",
|
|
13
13
|
{},
|
|
14
|
+
{ title: "List Title Blocks", readOnlyHint: true },
|
|
14
15
|
async () => {
|
|
15
16
|
try {
|
|
16
17
|
const result = await bridge.call("/titleblocks");
|
|
@@ -25,6 +26,7 @@ export function registerSheetTools(server, bridge) {
|
|
|
25
26
|
"revit_list_sheets",
|
|
26
27
|
"List the sheets in the active document: id, sheet number and name, ordered by sheet number.",
|
|
27
28
|
{},
|
|
29
|
+
{ title: "List Sheets", readOnlyHint: true },
|
|
28
30
|
async () => {
|
|
29
31
|
try {
|
|
30
32
|
const result = await bridge.call("/sheets");
|
|
@@ -59,6 +61,7 @@ export function registerSheetTools(server, bridge) {
|
|
|
59
61
|
"Title block family type id from revit_list_titleblocks. Omit it to use the first one loaded.",
|
|
60
62
|
),
|
|
61
63
|
},
|
|
64
|
+
{ title: "Create Sheets", readOnlyHint: false, destructiveHint: false },
|
|
62
65
|
async ({ sheets, title_block_id }) => {
|
|
63
66
|
try {
|
|
64
67
|
// The tool argument stays snake_case like every other tool's, but the
|
package/lib/tools/titleblocks.js
CHANGED
|
@@ -32,6 +32,7 @@ export function registerTitleblockTools(server, bridge) {
|
|
|
32
32
|
.int()
|
|
33
33
|
.describe("Id of the loaded title block TYPE (a FamilySymbol), from revit_list_titleblocks"),
|
|
34
34
|
},
|
|
35
|
+
{ title: "Inspect Title Block Family", readOnlyHint: true },
|
|
35
36
|
async ({ symbol_id }) => {
|
|
36
37
|
try {
|
|
37
38
|
// Tool arguments stay snake_case; the bridge reads symbolId.
|
|
@@ -119,6 +120,7 @@ export function registerTitleblockTools(server, bridge) {
|
|
|
119
120
|
"DEFAULTS TO TRUE. True resolves every id and text type and reports what it would do, changing nothing. Pass false to apply it and load the family back.",
|
|
120
121
|
),
|
|
121
122
|
},
|
|
123
|
+
{ title: "Edit Title Block Family", readOnlyHint: false, destructiveHint: true },
|
|
122
124
|
async ({
|
|
123
125
|
symbol_id,
|
|
124
126
|
expected_family_name,
|
package/lib/tools/views.js
CHANGED
|
@@ -21,6 +21,7 @@ export function registerViewTools(server, bridge) {
|
|
|
21
21
|
"revit_list_views",
|
|
22
22
|
"List the non-template views in the document: id, name, view type, and whether each is already placed on a sheet. A view with a frame also carries viewDirection, rightDirection and upDirection as {x,y,z} — viewDirection is Revit's direction towards the VIEWER, so a section looking north reports {x:0,y:-1,z:0}. Sheets themselves are not listed — use revit_list_sheets for those. A view that is already on a sheet cannot be placed on another one; duplicate it first with revit_duplicate_view.",
|
|
23
23
|
{},
|
|
24
|
+
{ title: "List Views", readOnlyHint: true },
|
|
24
25
|
async () => {
|
|
25
26
|
try {
|
|
26
27
|
const result = await bridge.call("/views");
|
|
@@ -51,6 +52,7 @@ export function registerViewTools(server, bridge) {
|
|
|
51
52
|
.optional()
|
|
52
53
|
.describe("View scale denominator, e.g. 100 for 1:100. Omit it to keep the type's default."),
|
|
53
54
|
},
|
|
55
|
+
{ title: "Create Plan View", readOnlyHint: false, destructiveHint: false },
|
|
54
56
|
async ({ level, name, view_family_type, scale }) => {
|
|
55
57
|
try {
|
|
56
58
|
// The tool arguments stay snake_case like every other tool's; the bridge
|
|
@@ -80,6 +82,7 @@ export function registerViewTools(server, bridge) {
|
|
|
80
82
|
.optional()
|
|
81
83
|
.describe("View scale denominator, e.g. 20 for 1:20. Omit it to keep the type's default."),
|
|
82
84
|
},
|
|
85
|
+
{ title: "Create Drafting View", readOnlyHint: false, destructiveHint: false },
|
|
83
86
|
async ({ name, scale }) => {
|
|
84
87
|
try {
|
|
85
88
|
const result = await bridge.call("/views/create-drafting", { name, scale });
|
|
@@ -122,6 +125,7 @@ export function registerViewTools(server, bridge) {
|
|
|
122
125
|
.optional()
|
|
123
126
|
.describe("View scale denominator, e.g. 50 for 1:50. Omit it to keep the type's default."),
|
|
124
127
|
},
|
|
128
|
+
{ title: "Create Section View", readOnlyHint: false, destructiveHint: false },
|
|
125
129
|
async ({ name, origin, direction, width, height, depth, scale }) => {
|
|
126
130
|
try {
|
|
127
131
|
const result = await bridge.call("/views/create-section", {
|
|
@@ -144,6 +148,7 @@ export function registerViewTools(server, bridge) {
|
|
|
144
148
|
"revit_list_legends",
|
|
145
149
|
"List the legend views in the document: id, name and scale. Read-only, and that is the point — Revit's API cannot author the first legend in a document, so this is the set revit_create_legend has to duplicate from. An empty list means a legend has to be made once in the Revit UI (View tab > Legends > Legend), or come from the template, before any legend can be created from here.",
|
|
146
150
|
{},
|
|
151
|
+
{ title: "List Legends", readOnlyHint: true },
|
|
147
152
|
async () => {
|
|
148
153
|
try {
|
|
149
154
|
const result = await bridge.call("/views/legends");
|
|
@@ -171,6 +176,7 @@ export function registerViewTools(server, bridge) {
|
|
|
171
176
|
.optional()
|
|
172
177
|
.describe("View scale denominator, e.g. 50 for 1:50. Omit it to keep the source legend's scale."),
|
|
173
178
|
},
|
|
179
|
+
{ title: "Create Legend", readOnlyHint: false, destructiveHint: false },
|
|
174
180
|
async ({ name, from_legend_id, scale }) => {
|
|
175
181
|
try {
|
|
176
182
|
// The tool arguments stay snake_case like every other tool's; the bridge
|
|
@@ -211,6 +217,7 @@ export function registerViewTools(server, bridge) {
|
|
|
211
217
|
.optional()
|
|
212
218
|
.describe("View scale denominator, e.g. 100 for 1:100. Ignored on a perspective view."),
|
|
213
219
|
},
|
|
220
|
+
{ title: "Create 3D View", readOnlyHint: false, destructiveHint: false },
|
|
214
221
|
async ({ name, eye, target, perspective, scale }) => {
|
|
215
222
|
try {
|
|
216
223
|
const result = await bridge.call("/views/create-3d", {
|
|
@@ -250,6 +257,7 @@ export function registerViewTools(server, bridge) {
|
|
|
250
257
|
"Not handled here. Send it to revit_set_view_graphics, which probes whether the shadows parameter is writable on that view and says which route it took. Passing it here fails the call rather than silently doing nothing.",
|
|
251
258
|
),
|
|
252
259
|
},
|
|
260
|
+
{ title: "Set View Display Style", readOnlyHint: false, destructiveHint: false },
|
|
253
261
|
async ({ view_id, style, detail_level, shadows }) => {
|
|
254
262
|
try {
|
|
255
263
|
const result = await bridge.call("/views/set-style", {
|
|
@@ -295,6 +303,7 @@ export function registerViewTools(server, bridge) {
|
|
|
295
303
|
"Full path of the backdrop image, required with kind 'image'. Revit reads it off disk every time it draws the view, so the file has to stay there.",
|
|
296
304
|
),
|
|
297
305
|
},
|
|
306
|
+
{ title: "Set View Background", readOnlyHint: false, destructiveHint: false },
|
|
298
307
|
async ({ view_id, kind, sky_color, horizon_color, ground_color, image_path }) => {
|
|
299
308
|
try {
|
|
300
309
|
// The tool arguments stay snake_case like every other tool's; the bridge
|
|
@@ -330,6 +339,7 @@ export function registerViewTools(server, bridge) {
|
|
|
330
339
|
.optional()
|
|
331
340
|
.describe("True (the default) hides them; false brings them back."),
|
|
332
341
|
},
|
|
342
|
+
{ title: "Hide Categories in View", readOnlyHint: false, destructiveHint: false },
|
|
333
343
|
async ({ view_id, categories, hidden }) => {
|
|
334
344
|
try {
|
|
335
345
|
const result = await bridge.call("/views/hide-categories", {
|
|
@@ -398,6 +408,7 @@ export function registerViewTools(server, bridge) {
|
|
|
398
408
|
"DEFAULTS TO TRUE. True reports each category's current override and the merged override that would be written, and changes nothing. Pass false to apply it.",
|
|
399
409
|
),
|
|
400
410
|
},
|
|
411
|
+
{ title: "Override Category Graphics", readOnlyHint: false, destructiveHint: false },
|
|
401
412
|
async ({ view_id, overrides, dry_run }) => {
|
|
402
413
|
const rows = overrides.map((row) => ({
|
|
403
414
|
category: row.category,
|
|
@@ -471,6 +482,7 @@ export function registerViewTools(server, bridge) {
|
|
|
471
482
|
.optional()
|
|
472
483
|
.describe("Time as HH:MM on a 24-hour clock, e.g. '15:30'. Puts the view in Still Image mode. Not to be combined with azimuth/altitude."),
|
|
473
484
|
},
|
|
485
|
+
{ title: "Set View Sun Position", readOnlyHint: false, destructiveHint: false },
|
|
474
486
|
async ({ view_id, azimuth, altitude, date, time }) => {
|
|
475
487
|
// Checked here rather than in the schema: a raw shape cannot express
|
|
476
488
|
// "one of these two groups", and a call with neither must not reach Revit.
|
|
@@ -540,6 +552,7 @@ export function registerViewTools(server, bridge) {
|
|
|
540
552
|
.optional()
|
|
541
553
|
.describe("PNG (the default), JPEG, JPEGLossless, JPEGMedium, JPEGSmallest, BMP, TIFF or TARGA"),
|
|
542
554
|
},
|
|
555
|
+
{ title: "Export View Image", readOnlyHint: false, destructiveHint: false },
|
|
543
556
|
async ({ view_id, view_ids, path, width, height, format }) => {
|
|
544
557
|
try {
|
|
545
558
|
if (view_id === undefined && !view_ids) {
|
|
@@ -573,6 +586,7 @@ export function registerViewTools(server, bridge) {
|
|
|
573
586
|
.optional()
|
|
574
587
|
.describe("Revit's ViewDuplicateOption. Defaults to Duplicate."),
|
|
575
588
|
},
|
|
589
|
+
{ title: "Duplicate View", readOnlyHint: false, destructiveHint: false },
|
|
576
590
|
async ({ view_id, name, detailing }) => {
|
|
577
591
|
try {
|
|
578
592
|
const result = await bridge.call("/views/duplicate", {
|
|
@@ -607,6 +621,7 @@ export function registerViewTools(server, bridge) {
|
|
|
607
621
|
.positive()
|
|
608
622
|
.describe("View scale denominator, e.g. 100 for 1:100. Revit's range is 1 to 24000."),
|
|
609
623
|
},
|
|
624
|
+
{ title: "Set View Scale", readOnlyHint: false, destructiveHint: false },
|
|
610
625
|
async ({ view_id, view_ids, scale }) => {
|
|
611
626
|
// Checked here rather than in the schema: a raw shape cannot express
|
|
612
627
|
// "one of these two", and a call with neither must not reach Revit.
|
|
@@ -659,6 +674,7 @@ export function registerViewTools(server, bridge) {
|
|
|
659
674
|
"DEFAULTS TO TRUE. True reports the view's current size and the multiplier asked for, and changes nothing. Pass false to apply it.",
|
|
660
675
|
),
|
|
661
676
|
},
|
|
677
|
+
{ title: "Resize Perspective View", readOnlyHint: false, destructiveHint: false },
|
|
662
678
|
async ({ view_id, multiplier, dry_run }) => {
|
|
663
679
|
try {
|
|
664
680
|
const result = await bridge.call("/views/scale-perspective-crop", {
|
|
@@ -695,6 +711,7 @@ export function registerViewTools(server, bridge) {
|
|
|
695
711
|
.min(1)
|
|
696
712
|
.describe("Placements to make, all in this one call"),
|
|
697
713
|
},
|
|
714
|
+
{ title: "Place Views on Sheets", readOnlyHint: false, destructiveHint: false },
|
|
698
715
|
async ({ placements }) => {
|
|
699
716
|
try {
|
|
700
717
|
// The tool arguments stay snake_case like every other tool's; the bridge
|
|
@@ -734,6 +751,7 @@ export function registerViewTools(server, bridge) {
|
|
|
734
751
|
.optional()
|
|
735
752
|
.describe("View scale denominator. Schedules have no meaningful scale; omit it unless you know otherwise."),
|
|
736
753
|
},
|
|
754
|
+
{ title: "Create Schedule", readOnlyHint: false, destructiveHint: false },
|
|
737
755
|
async ({ category, name, fields, scale }) => {
|
|
738
756
|
try {
|
|
739
757
|
const result = await bridge.call("/schedules/create", { category, name, fields, scale });
|
package/lib/tools/write.js
CHANGED
|
@@ -22,6 +22,7 @@ export function registerWriteTools(server, bridge) {
|
|
|
22
22
|
.min(1)
|
|
23
23
|
.describe("Levels to create"),
|
|
24
24
|
},
|
|
25
|
+
{ title: "Create Levels", readOnlyHint: false, destructiveHint: false },
|
|
25
26
|
async ({ levels }) => {
|
|
26
27
|
try {
|
|
27
28
|
const result = await bridge.call("/levels/create", { levels });
|
|
@@ -52,6 +53,7 @@ export function registerWriteTools(server, bridge) {
|
|
|
52
53
|
.min(1)
|
|
53
54
|
.describe("Wall centrelines in plan, in feet. One wall per curve."),
|
|
54
55
|
},
|
|
56
|
+
{ title: "Create Walls", readOnlyHint: false, destructiveHint: false },
|
|
55
57
|
async ({ level, wall_type, height, curves }) => {
|
|
56
58
|
try {
|
|
57
59
|
// The tool argument stays snake_case like every other tool's, but the
|
|
@@ -86,6 +88,7 @@ export function registerWriteTools(server, bridge) {
|
|
|
86
88
|
"Revit's own parameter id, for when the name matches more than one: negative for a built-in (e.g. -1002062 SCHEDULE_LEVEL_PARAM), positive for a shared or project parameter. Must be a parameter of the name given. Only needed when the name is ambiguous.",
|
|
87
89
|
),
|
|
88
90
|
},
|
|
91
|
+
{ title: "Set Parameters", readOnlyHint: false, destructiveHint: false },
|
|
89
92
|
async ({ ids, name, value, parameter_id }) => {
|
|
90
93
|
try {
|
|
91
94
|
// Left out entirely when it was not given: the bridge only asks which
|
|
@@ -110,6 +113,7 @@ export function registerWriteTools(server, bridge) {
|
|
|
110
113
|
{
|
|
111
114
|
ids: z.array(z.number().int()).min(1).describe("Element ids to delete"),
|
|
112
115
|
},
|
|
116
|
+
{ title: "Delete Elements", readOnlyHint: false, destructiveHint: true },
|
|
113
117
|
async ({ ids }) => {
|
|
114
118
|
try {
|
|
115
119
|
const result = await bridge.call("/elements/delete", { ids });
|
package/manifest.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"manifest_version": "0.2",
|
|
3
3
|
"name": "revit-mcp",
|
|
4
4
|
"display_name": "Revit",
|
|
5
|
-
"version": "1.0.
|
|
5
|
+
"version": "1.0.2",
|
|
6
6
|
"description": "Query and edit the Autodesk Revit model open on your desktop.",
|
|
7
7
|
"long_description": "Connects Claude to the Revit model currently open on this Windows machine through a bundled C# bridge add-in. Query levels and elements, read and write parameters, model geometry, and produce a full drawing set: sheets, views, schedules and PDF exports. Requires Autodesk Revit 2025, 2026 or 2027, and a one-time bridge install run from inside Claude.",
|
|
8
8
|
"author": {
|
|
@@ -412,5 +412,8 @@
|
|
|
412
412
|
"runtimes": {
|
|
413
413
|
"node": ">=18.0.0"
|
|
414
414
|
}
|
|
415
|
-
}
|
|
415
|
+
},
|
|
416
|
+
"privacy_policies": [
|
|
417
|
+
"https://github.com/rui-branco/revit-mcp/blob/main/PRIVACY.md"
|
|
418
|
+
]
|
|
416
419
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rui.branco/revit-mcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "MCP server for Autodesk Revit: read and edit the open model from an MCP client, via a bundled C# bridge add-in that runs inside Revit",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"revit",
|
|
@@ -32,7 +32,8 @@
|
|
|
32
32
|
"revit-bridge/dist/",
|
|
33
33
|
"README.md",
|
|
34
34
|
"LICENSE",
|
|
35
|
-
"manifest.json"
|
|
35
|
+
"manifest.json",
|
|
36
|
+
"PRIVACY.md"
|
|
36
37
|
],
|
|
37
38
|
"engines": {
|
|
38
39
|
"node": ">=18"
|
|
Binary file
|
|
Binary file
|