Model MCP: manual end-to-end test plan¶
Full manual validation checklist. Goal: exercise every tool and every install/transport path, from a clean install through live model reads and gated actions, on each supported Revit version.
Run when scheduled. Tick each [ ]. For every checked item record: result (pass/fail), evidence (journal line, screenshot, tool JSON, elapsed), and notes. File a GitHub issue for each fail with the repro.
Reference model for expected values: Snowdon Towers Sample Architectural.rvt (ships with Revit). Known values captured 2026-09-15 on Revit 2024.3 — use as the expected baseline where cited. If testing another model, capture its own baseline first with
revit_document_info+revit_aggregate_elements.
0. Test environments (matrix)¶
Ideally test on two machines:
- M1 — clean-ish Windows workstation: fresh or minimally-used Windows, target Revit version installed, Claude Desktop installed and logged in. This is the "does a real user get it working from the installers" run.
- M2 — remote client (macOS/Linux): Claude Desktop / Claude Code on a Mac, reaching a Windows workstation's Revit over the remote (SSH tunnel) transport.
| Field | M1 | M2 |
|---|---|---|
| OS / build | ||
| Revit version(s) | ||
| Python + uv version | ||
| Claude Desktop version | ||
Claude Code (claude --version) |
||
| Release under test (add-in / server / mcpb) |
Record the exact release tag being tested (e.g. v0.5.0) and confirm all three artifacts are the same version: the .msi/add-in zip, the PyPI revit-model-mcp, and the .mcpb.
1. Add-in installation (fresh, per Revit version)¶
Do this with Revit closed. Repeat the whole section for each Revit year present (2022 / 2023 / 2024 / 2025 / 2026 / 2027).
- [ ] 1.1 MSI — SingleUser: run
RevitModelMcp-<ver>-SingleUser.msi. Installer completes without error. - [ ] 1.2 MSI — MultiUser: on a separate profile/machine, run
RevitModelMcp-<ver>-MultiUser.msi. Completes; add-in visible for all users. - [ ] 1.2a MSI scope conflict: install SingleUser, then run MultiUser: it is refused with the "SingleUser ... already installed for this user" message. Uninstall SingleUser, install MultiUser, then run SingleUser: it is refused with the "MultiUser ... already installed on this computer" message. Uninstall each; no add-in files remain.
- [ ] 1.3 Clone script:
./install.ps1 -Source Releasefrom a clone. Completes. - [ ] 1.4 Manifest present:
%APPDATA%\Autodesk\Revit\Addins\<year>\RevitModelMcp.addinexists and points to aRevitModelMcp.dllthat exists. - [ ] 1.5 DLL version: the installed
RevitModelMcp.dllmatches the release version. - [ ] 1.6 Provenance: verify the release asset's build-provenance attestation (per docs
security/#verify-downloads) before install. - [ ] 1.7 Load in Revit: start Revit, accept the "unsigned add-in" trust dialog (Always Load), open a model. Journal (
%LOCALAPPDATA%\Autodesk\Revit\Autodesk Revit <year>\Journals\journal.*.txt) showsAPI_SUCCESS ... Starting External Application: Model MCP ... Assembly Version: <ver>and event registrations. No fatalAPI_ERRORfor the MCP DLL (benign "assembly version conflict" warnings, where the reference is lower than the installed Revit build, are OK). - [ ] 1.8 Uninstall: MSI uninstall removes the manifest cleanly (no orphan files, no load error next Revit start).
Regression to watch (found before): the add-in must reference RevitAPI/RevitAPIUI pinned to <year>.0.0.0 (≤ installed build) and System.Diagnostics.DiagnosticSource 8.x, or it fails to load. Confirm per version.
Expected journal line: Revit 2022 and 2025 journals can log API_ERROR: Assembly version conflict in some references in RevitModelMcp.dll. It is not a pinning fault and the add-in loads and works. The pinned RevitAPI, RevitAPIUI, UIFramework, UIFrameworkServices and AdWindows references are equal to or older than the installed Revit assemblies (for example RevitAPI 25.0.0.0 against 25.5.0.0 on 2025; 22.0.0.0 against 22.0.0.0 on 2022). The remaining differences are the add-in's own System.* and Microsoft.CodeAnalysis dependencies, which differ from the copies Revit ships (Revit 2022 ships System.Collections.Immutable 1.2.2.0 and System.Memory 4.0.1.0; the add-in references 8.0.0.0 and 4.0.1.2 and loads its own copies from the plugin folder). Other add-ins log the same line. Treat it as a failure only when the add-in does not start.
2. Server + client wiring (every documented method)¶
2.1 Local server via uvx (Windows workstation)¶
- [ ]
uvx revit-model-mcpresolves and installs the package from PyPI on first run (record package count + time). - [ ] Server starts and reports
serverInfo: { name: "Model MCP", version: "<ver>" }oninitialize. - [ ]
tools/listreturns 19 read tools (enumerate — see §3).
2.2 Claude Code registration (claude mcp add)¶
- [ ]
claude mcp add revit-model-mcp -s user -e REVIT_MCP_HOST=local -e REVIT_MCP_REDACT_PATHS=1 -- uvx revit-model-mcpwrites to~/.claude.json. - [ ]
claude mcp listshowsrevit-model-mcp: ... ✓ Connected. - [ ]
claude -p "what model is open in Revit?"(headless) returns real model data (capture stdout to a file — headless output can be lost over a raw pipe). - [ ] App local agent: in Claude Desktop's Code surface with the "Local" chip, ask a Revit question → agent calls
mcp__revit-model-mcp__*and answers with real data.
2.3 Claude Desktop .mcpb one-click install ⚠️ known-broken, re-verify¶
- [ ] Double-clicking
revit-model-mcp-<ver>.mcpbon Windows opens it in Claude Desktop (file association). 2026-09-15: FAILED — Windows showed "choose an app", Claude not registered as the.mcpbhandler. Confirm whether fixed / document the correct install entry point (Settings → Extensions/Connectors → install from file). - [ ] Install via Claude Desktop Settings → Extensions/Connectors → install
.mcpb; the config form setsREVIT_MCP_HOST(local/remote), redaction, actions, HTTP token without editing JSON. - [ ] After install the connector appears and its tools are usable in a surface that can reach a local stdio server.
2.4 Manual claude_desktop_config.json ⚠️ surface limitation, re-verify¶
- [ ] Write the documented
mcpServersblock, restart Claude Desktop. - [ ] Determine which surface picks it up. 2026-09-15: the app's cloud chat could NOT reach the local stdio MCP ("doesn't reach this cloud session"). Document clearly: local stdio MCP works only in the local agent, not the cloud chat.
2.5 Remote workstation (M2 → M1)¶
- [ ] From a Mac/Linux client, configure the remote workstation (SSH tunnel to the workstation loopback endpoint) per docs
transport/. - [ ] Client
revit_pingsucceeds against the remote Revit; a read tool returns real data.
3. Read tools — full coverage (19)¶
Model open on the workstation. Call revit_list_catalog first, revit_aggregate_elements second, revit_query_elements only when rows are needed (per server guidance). For each tool: record the JSON, confirm success: true, spot-check values.
| # | Tool | How to invoke (natural prompt or direct call) | Expected / spot-check | [ ] |
|---|---|---|---|---|
| 1 | revit_ping |
"is Revit reachable?" | pong; responder documentName + revitVersion + processId |
[ ] |
| 2 | revit_document_info |
"what model is open, its levels and rooms?" | file name; levels with elevation + roomCount; area schemes (Snowdon: Gross Building 14, Rentable 87); isWorkshared |
[ ] |
| 3 | revit_model_health |
"give me a model health summary" | health metrics (warning count, etc.) return without error | [ ] |
| 4 | revit_links_status |
"list linked models and their status" | linked model list (empty is a valid result — confirm shape) | [ ] |
| 5 | revit_shared_coordinates |
"what are the shared coordinates / survey point?" | survey/base point + coordinate system | [ ] |
| 6 | revit_parameter_fill_check |
"check parameter completeness" | fill/missing report for the requested parameters/category | [ ] |
| 7 | revit_list_catalog |
"list the element categories/types in the model" | catalog of categories and counts | [ ] |
| 8 | revit_aggregate_elements |
"how many doors and windows?" | counts by category (Snowdon: Doors 142, Windows 106) | [ ] |
| 9 | revit_query_elements |
"find the largest room: name, level, area" | rows with real values (Snowdon: Parking Garage, level Parking, 957.6 m²) | [ ] |
| 10 | revit_list_views |
"list the views" | views enumerated (browser has floor/ceiling/3D/elevation/section) | [ ] |
| 11 | revit_view_summary |
"summarize the active view / view X" | view type, scale, element count summary | [ ] |
| 12 | revit_export_view |
"export the {3D / a floor plan} view to an image" | image produced; localPath returned (visible even with redaction) — open it, confirm it renders |
[ ] |
| 13 | revit_view_elements |
"what elements are in view X?" | element list for that view | [ ] |
| 14 | revit_element_details |
"details of element |
full parameter details for that element | [ ] |
| 15 | revit_view_warnings |
"warnings in view X" | warnings scoped to the view | [ ] |
| 16 | revit_list_warnings |
"list all model warnings" | model-wide warnings list | [ ] |
| 17 | revit_list_relations |
"show hosting/group relations for element |
relation graph (host, hosted, group membership) | [ ] |
| 18 | revit_list_instances |
"list instances of family/type |
instances of the named type | [ ] |
| 19 | revit_family_audit |
"audit family |
parameter use, shared flag and purge coverage; no project change | [ ] |
- [ ] 3.20 Redaction on: with
REVIT_MCP_REDACT_PATHS=1, path fields are redacted but names, parameter values, errors, channel files and exportlocalPathremain visible. - [ ] 3.21 Redaction off:
REVIT_MCP_REDACT_PATHS=0shows full paths.
4. Action tools — gated writes (11)¶
Family audit and edits¶
- [ ] Open an
.rfa. Runrevit_family_auditwithoutfamilies; inspect shared status, parameter use and purge counts. Runrevit_edit_familieswith an added shared parameter, then save manually. - [ ] In a project, audit three editable families. Run the same edit with
dry_run=true; verify the project is unchanged and no family was loaded. - [ ] Run the edit for real; verify one
revit_edit_familiesundo entry. Undo and verify that the original family state returns. - [ ] Run
set_sharedand confirm the flag in Family Category and Parameters. Check the reported loaded state. - [ ] Run
purgeon Revit 2023 and verify coveragefamilies-and-types; on Revit 2026 verifyfull. - [ ] Verify a used parameter is kept with
usedBy, an unused shared parameter requiresinclude_shared=true, and a missing shared parameter file fails before any edit. - [ ] With
replace_family_parameter=true, give a valid GUID and a different parameter name. Verify the edit fails and the original parameter and its values remain intact. - [ ] Cause the first family edit to fail with
stop_on_error=true. Verifysuccess:false,committed:false,failedFamily, androlledBack:true; confirm the project is unchanged. - [ ] Audit enough families for the call to take over 60 seconds. Verify the complete audit returns
success:truewithin the configured response timeout.
Actions run by default; the single workstation read-only lock (Read-only ribbon button or revit_set_read_only) switches them off, and the machine policy wins. Direct HTTP callers also need the bearer token.
NWC export¶
- [ ] Export a real XML from Navisworks Settings on the owner’s workstation. Compare
nwexportrevit_element_params:0|1|2,nwexportrevit_section_extract:0|1|2andnwexportrevit_coordinates:0|1against the displayed settings; correct the three mapping arrays inNwcSettingsXmlif the order differs. - [ ] Run
revit_nwc_settings_checkwith that XML. Confirm mapped values,notAppliedfor the four API-unsupported options, andignoredfor unknown IDs. Confirm no NWC is created. -
[ ] Run
revit_export_nwcwithsettings_xmlanddry_run=true, then override one XML value explicitly. Confirmoptions.sourcesreportsxmlandargumentrespectively and that unspecified values reportdefault. -
[ ] Remove or disable the year-matched Navisworks NWC exporter, or reproduce an exporter startup failure.
revit_export_nwcreportsNavisworks exporter is not available in Revit <year> on this workstation (not installed or failed to load at startup). - [ ] With the exporter installed, run
dry_run=truewith a new absolute.nwcpath. Check all effective options andexporterAvailable:true; confirm no file is created. - [ ] Export the full model with
coordinates="shared". Open it in Navisworks and verify alignment with an NWC of a linked model exported the same way. - [ ] Export a non-template 3D view with
scope="view"and an enabled section box. Verify the NWC respects the section box. - [ ] Export a 3D view whose exact name is numeric. Verify name lookup works when that number is not a view ID.
- [ ] Export once with
parameters="all"and once withparameters="none". Verify element properties disappear in the second NWC. -
[ ] Export to an existing file without
overwrite. Verify the error and confirm the original file bytes are unchanged. -
[ ] 4.0 Enumerate: start the server and run
tools/list— record the exact action tool names and count. Known action set from the demo recording (README "In action"): open a view, select element(s), isolate, place a family instance, move an element, and cleanup/undo. Fill the table with the real names.
| Action tool (fill from tools/list) | Test | Verify | Cleanup | [ ] |
|---|---|---|---|---|
| open view | open a specific view by name | view is active in Revit UI | — | [ ] |
| select element(s) | select the largest room / an element by id | selection highlighted in Revit | clear selection | [ ] |
| isolate | isolate the selection in the view | temp isolate active | reset temporary hide/isolate | [ ] |
| place family instance | place e.g. a chair at a point | instance exists at location | delete it | [ ] |
| move element | move the placed instance | new location correct | delete it | [ ] |
| delete / cleanup | delete the test instance | element gone | model back to baseline | [ ] |
| (others) | [ ] |
- [ ] 4.1 Gate negative test: with the Read-only ribbon button on, an action is refused with
errorCode:"read_only"; the tool stays listed and read tools still work. Turning it off withrevit_set_read_onlyfirst returns a confirmation token and changes nothing until the user confirms. - [ ] 4.2 HTTP token gate: a direct HTTP action without the bearer token is refused;
/healthworks without token. - [ ] 4.3 Transaction safety: every action wraps a Revit transaction; a failed action leaves the model unchanged (no partial edits).
- [ ] 4.4 Model restored: after the action suite, the model matches its pre-test baseline (re-run
revit_aggregate_elements).
Link datum alignment¶
- [ ] Open a host model and a loaded link with a known 25 mm grid shift and 150 mm level shift.
revit_compare_link_datumsreports those deltas without changing the host. - [ ] Run
revit_align_link_datums(dry_run=true). Shifted datums reportmoved; inspect the host and confirm it is unchanged. - [ ] Run the real alignment. Confirm grids and levels move to the link geometry; then use Revit Undo and confirm their original positions return.
- [ ] Pin a shifted grid. With
include_pinned=false, confirmskippedwith reasonpinnedand no movement. - [ ] Give a coincident host grid a different name. Confirm the read result says
matchedBy:"geometry"and the action does not create a duplicate.
5. Transports¶
- [ ] 5.1 Local (
REVIT_MCP_HOST=local) on the workstation — all of §3 passes. - [ ] 5.2 SSH tunnel (remote client → workstation loopback) —
revit_ping+ a read tool pass from M2. - [ ] 5.3 HTTP (loopback + bearer token) —
/health(no token) responds; an authenticated read works. ⚠️ Re-verify issue #44: the HTTP listener (port 53110) not binding. Confirm fixed or still open. - [ ] 5.3a HTTP health and tool call: with HTTP enabled,
curl -i http://127.0.0.1:<port>/healthreturns200and a JSON body whosecommandsis a non-empty list. Then run one tool (for examplerevit_ping) withREVIT_MCP_HOST=http://127.0.0.1:<port>and a validREVIT_MCP_TOKEN; it succeeds instead ofRevit endpoint returned HTTP 500. - [ ] 5.4 Concurrency / channel contention ⚠️ found 2026-09-15: fire several tool calls in quick succession (as the agent does when reasoning). Earlier run showed 4 of 7 parallel calls failing (single file-channel "busy: trigger.txt exists"). Test rapid/parallel calls and record the failure rate; if the channel serializes poorly, file an issue.
6. Multi-version Revit¶
Repeat the §3 read smoke set (at minimum: ping, document_info, list_catalog, aggregate_elements, list_warnings, export_view) with a model open in each installed version:
- [ ] 6.1 Revit 2022 (net48)
- [ ] 6.2 Revit 2023 (net48)
- [ ] 6.3 Revit 2024 (net48)
- [ ] 6.4 Revit 2025 (net8)
- [ ] 6.5 Revit 2026 (net8)
- [ ] 6.6 Revit 2027 (net10) — if available
- [ ] 6.7 Two instances open: with 2+ Revit versions running, confirm the
documentparameter is required and correctly routes to the intended instance (revit_pingwith/withoutdocument).
7. Client-surface matrix (which Claude surface actually works)¶
Record pass/fail per surface — this is where the confusion was:
| Surface | Reaches local stdio MCP? | Reaches remote MCP? | [ ] |
|---|---|---|---|
| Claude Desktop — cloud chat (Chat/Cowork) | expected NO (cloud) | via connector? | [ ] |
| Claude Desktop — local agent (Code + "Local" chip) | expected YES | n/a | [ ] |
Claude Code CLI (claude -p, claude) local |
expected YES | n/a | [ ] |
| Claude Desktop with remote workstation config (M2) | n/a | expected YES | [ ] |
- [ ] Document the recommended setup for each user type (Windows-local vs Mac-remote) based on what actually passed.
8. Known-issue regression (must re-test)¶
Explicit re-tests for things that broke or surprised us:
- [ ] 8.1
.mcpbfile association / open-in-Claude (see §2.3). - [ ] 8.2 Local stdio MCP unavailable in the cloud chat (see §2.4).
- [ ] 8.3 HTTP listener 53110 bind — issue #44 (see §5.3).
- [ ] 8.4 Channel contention on parallel tool calls (see §5.4).
- [ ] 8.5 Add-in load: benign vs fatal
API_ERROR(assembly version conflict) per version (see §1.7). - [ ] 8.6
uvx/claudenot on the non-interactive shell PATH (use full paths) — a setup-doc note, not a product bug.
Document lifecycle validation¶
- [ ] Call
revit_documentswith no active model, then with active and background models; check every state field andopenedByMcp.
Use disposable local and Revit Server test models. Check each operation in Revit 2022 through 2027 where available.
- [ ] Open a non-workshared file in the background, then activate another document and close it.
- [ ] Open local and RSN central models with both detached modes. Confirm the central file remains unchanged.
- [ ] Create a local copy and verify its destination under
%LOCALAPPDATA%\RevitModelMcp\locals; refuse an existing destination. - [ ] Open all, no and named worksets; verify the returned
worksetsOpennames. - [ ] Reject a cloud path, malformed RSN path, and
read_only_localon a central or writable file. - [ ] Confirm that an active document cannot be closed and a document with an open transaction cannot be closed, saved or synchronized.
- [ ] Request save, sync and close-with-loss without a token. Confirm no change occurs. Retry after chat approval and verify the exact change.
- [ ] Retry a consumed, expired or argument-mismatched token and verify no change.
- [ ] Refuse a detached sync, a family sync and any save-as destination matching a known central path.
- [ ] Hold the central lock during sync and verify a clear error without indefinite waiting.
- [ ] Record suppressed dialogs and warnings from a disposable model open; confirm unrelated failures remain visible.
View preparation smoke test¶
- [ ] In a detached workshared model, call
revit_view_infoby name and ID. Check the template controls, 3D background, section box, hidden categories, worksets, filters and links against the Revit UI. - [ ] Run
revit_set_view_visibilitywithdry_run=true; confirm before/after values and no persisted changes. Repeat on an assigned template withouttemplate_mode(expect rejection), then with each mode and inspect affected views. - [ ] Hide category types and named categories, and hide worksets with both glob and
regex:masks. Confirm unmatched category suggestions and per-category failures. - [ ] Run
revit_remove_linkswithdry_run=true, then on a disposable detached copy. Verify type and instance counts, untouched imported CAD by default, and the central-connected refusal and local-copy sync warning. - [ ] Repeat link inspection on Revit 2022–2023 and 2024+ to check the override fallback and
GetLinkOverridespath.
9. Reporting¶
For the run, produce a short results table: environment, release tag, per-section pass/fail counts, and a list of filed issues. Optionally record a clean demo video — but arrange the UI first (see the preferences note): open a real 3D/model view in Revit (not just the project browser), size Claude and Revit panels so both are legible, then start the screen recording. Ask the owner to set the layout before recording.
Definition of done for the release: §1–§7 all pass on at least Revit 2024 + 2026 (local) and one remote (M2) run; all §8 regressions either pass or have a tracked issue; the client-surface matrix (§7) is documented in the README/docs.
Workstation regression and release smoke¶
The workstation must have a licensed Revit installation, .NET from global.json, uv, and an interactive user session.
Both scripts refuse to start while another Revit process exists.
Shared workstation runs use only rmm-* tasks and never stop another session's Revit or scheduled tasks.
.\build\run-addin-tests.ps1 -Year 2027
.\build\run-addin-tests.ps1 -Year 2022 -Model 'C:\Projects\rmm-test\2022\rac_basic_sample_project.rvt'
.\build\release-smoke.ps1 -Msi '.\output\RevitModelMcp-1.0.0-SingleUser.msi' -ExpectedVersion '1.0.0'
The regression executable opens private model copies and exercises catalog limits, aggregation, query filters and paging, skipped reads, details, warnings, health counts, parameter fill, family audit summary/full, API lookup, view listing, and readers after another document closes.
Action tests set a parameter, move, copy, and delete a seeded wall; each checks the exact summary, named group contract, distinct action transaction name, and model result before rolling back the enclosing transaction group.
The API fixture checks document lifetime recovery; interactive active-document switching remains a manual check.
The original sample model is never saved.
TRX reports appear under C:\Projects\rmm-test\out\tests; the runner rejects fewer than 15 passes or any failure.
Record the counters for each year and retain the TRX files with workstation evidence.
The release smoke requires a SingleUser MSI.
An existing registered SingleUser installation also requires -PreviousMsi <full previous package>; its ProductCode must match the installed product.
The smoke restores the previous MSI registration, add-in files, and settings after removing the tested package.
It retains a dated backup and checks per-year manifests and ProductVersions, a fresh 2027 heartbeat for the selected model, and a successful revit_ping over the named pipe.
After the run, inspect summary.txt or summary.csv in the dated smoke output directory.
Every restore row must pass; compare the restored DLL ProductVersions with the integration build and confirm that no owned Revit process or scheduled task remains.
An installation failure still triggers cleanup and restoration.
The backup remains available if cleanup fails.
See release ritual for the release gate and packaging command.