Contributing¶
Bug reports and feature requests use the issue forms. Bug reports include the Revit year, add-in version, installation method, transport, reproduction steps and sanitized logs. For visible failures, attach a screenshot of the dialog or ribbon in the form's screenshot field. Usage and installation questions belong in Discussions.
Contributors follow the code of conduct.
Start in 10 minutes¶
Pick a good first issue, comment that you are taking it, and open a PR from a fork as described below.
Issues labeled help wanted are open to anyone; larger ones are tracked as epic with child issues.
Contributing without Revit¶
You do not need Revit or Windows for the Python server, the Core library, tests and documentation:
cd server && uv run --with pytest pytest -q
dotnet test --project tests/RevitModelMcp.Core.Tests/RevitModelMcp.Core.Tests.csproj
uv tool install pre-commit && pre-commit install
pre-commit run --all-files
The first command runs the Python server tests, which use mocked transports and a fake HTTP server.
The second runs the Core C# tests on Windows, macOS or Linux with the SDK from global.json.
The dotnet-format hook prints a notice and exits successfully on macOS and Linux.
Windows PR checks verify C# formatting and build the add-in for every Revit year.
Only changes under src/RevitModelMcp.Addin need Windows to build locally.
Labels and milestones¶
- Type:
bug,enhancement,docs,tests,serverand similar labels group the work and the release notes. - Priority:
P0crash, data safety or wrong result;P1real bug on a common path;P2edge case or robustness;P3polish or rare case. needs-designmeans the approach must be agreed in the issue before code.- Milestones follow the weekly release: a milestone named by date, for example
2026-10-12, holds what ships that Monday, andBacklogholds unscheduled work.
Pull requests¶
- Open an issue before a large change and agree on the expected behavior.
- Fork the repository, clone the fork and create a focused branch from
main, for examplegit switch -c fix/http-timeout. - Make the change and run the relevant checks below.
- Use Conventional Commits for commits and the PR title, for example
fix(server): handle connection timeoutsorfeat(addin): expose view metadata. - Push the branch and open a PR against
main; fill in the summary, linked issue and checklist. - For any UI or ribbon change, drag and drop a screenshot or recording into the PR's validation section, or paste an image from the clipboard.
- Remove credentials, private model names and paths from attachments and logs.
The PR checklist covers supported Revit builds, tests, documentation, screenshots and secrets. Include commands, results and affected Revit years in the validation section; state when a checklist item does not apply. Use English for code and public API descriptions.
main requires a PR and passing required checks on an up-to-date branch.
There is no blanket approval requirement.
Owner review applies to the paths in CODEOWNERS:
- Add-in and Core source under
src/, except project dependency manifests. - Python runtime code under
server/revit_model_mcp/. - Installer code under
build/install/and the root installation script. - Release, release-please, WinGet and Dependabot auto-merge workflows.
- CODEOWNERS, the security policy and the license.
Docs, tests, other CI workflows and dependency manifests without an owner can merge after required checks pass.
Dependabot patch and minor updates enable squash auto-merge; required checks and any owner review still apply.
Major updates receive a needs-review label and wait for a maintainer.
NuGet manifests under build/install/ and publishing workflow updates still require owner review.
All merges use squash with the PR title and body, and history remains linear.
Automated checks¶
CI builds the Revit 2022, 2026 and 2027 add-ins, runs Core and Python tests, builds and smoke-tests both MSI scopes, and validates the Python package.
PR checks validate the Conventional Commit title, all workflow files with actionlint, C# formatting from .editorconfig, and Python lint and formatting with Ruff.
CodeQL analyzes C# and Python on PRs, pushes to main and a weekly schedule.
Successful PR checks publish one updated comment with add-in artifact links and the Revit years built.
Artifact downloads require a GitHub login and expire after 90 days.
The shared community.yml workflow applies path labels, welcomes contributors and handles stale issues and PRs.
The shared dependabot-auto-merge.yml workflow handles dependency update review and auto-merge.
PR checks call the shared check-failure-comment.yml workflow to maintain one failure comment with reproduction commands for same-repository PRs other than Dependabot.
These workflows are maintained in sharafutdinovdi/.github.
Path labels are applied automatically; enhancement, bug, docs and dependencies group generated release notes.
Local checks¶
Install uv, Python 3.11+, and the .NET SDK selected by global.json.
The actionlint hook installs version 1.7.12 in an isolated Go environment; Go must be available for the initial setup.
Run from the repository root:
uv tool install pre-commit
pre-commit install
pre-commit run --all-files
The hooks apply Ruff 0.16.10 fixes and formatting, verify C# formatting, lint workflows, and check file endings, whitespace, YAML and JSON.
The C# hook sets Configuration=Debug.R26 and DeployAddin=false and runs once when C# files change.
Full solution formatting requires Windows.
On macOS and Linux, the hook prints a notice and exits successfully; Windows PR CI verifies C# formatting.
Review hook edits and stage them before committing again.
Build and test¶
Run from a fresh clone's repository root on Windows with the .NET SDK selected by global.json:
foreach ($year in '22', '23', '24', '25', '26', '27') {
dotnet build src/RevitModelMcp.Addin -c "Release.R$year" -p:DeployAddin=false
if ($LASTEXITCODE -ne 0) { throw "R$year build failed" }
}
dotnet test --project tests/RevitModelMcp.Core.Tests/RevitModelMcp.Core.Tests.csproj
$env:Configuration = 'Debug.R26'
$env:DeployAddin = 'false'
dotnet format RevitModelMcp.sln --verify-no-changes --verbosity minimal
DeployAddin=false prevents deployment to the local Revit installation.
Core tests need no running Revit instance.
The test runner is Microsoft.Testing.Platform; use --project as shown.
Release builds cover Release.R22 through Release.R27.
Run dotnet format RevitModelMcp.sln with the same environment variables to apply formatting.
Run Python tests and package builds on Windows, macOS or Linux with Python 3.11+ and uv:
cd server
uv run --with pytest pytest -q
uvx ruff==0.16.10 check .
uvx ruff==0.16.10 format --check .
uv build
uvx twine check dist/*
Transport tests use mocked operations and a local fake HTTP server.
They do not require a Windows workstation.
Keep credentials and model files out of commits and use sanitized fixtures.
Run uvx ruff==0.16.10 check --fix . and uvx ruff==0.16.10 format . to apply Python lint fixes and formatting.
Run actionlint 1.7.12 from the repository root after changing a workflow.
Documentation site¶
The documentation site uses MkDocs Material and includes the repository and server READMEs. Preview it from the repository root:
python3 -m venv .venv
.venv/bin/python -m pip install -r docs/requirements.txt
.venv/bin/mkdocs serve
On Windows, use .venv\Scripts\python.exe and .venv\Scripts\mkdocs.exe.
Open http://127.0.0.1:8000/revit-model-mcp/.
Run mkdocs build --strict in the activated environment before submitting a PR.
Test coverage¶
The Python tests cover job construction, transport failures, downloads, action validation and MCP stdio registration with both flag states.
They also check that the bundle manifest lists every registered tool in order with its current description.
After changing tools, run cd server && uv run python ../build/bundle_manifest.py to regenerate the manifest.
The same tests compare docs/tool-manifest.json with the registry and check the policy tables behind it.
After changing tools, also run cd server && uv run python ../build/tool_manifest.py; see Tool manifest.
A committed snapshot of every tool input schema (server/tests/golden/tools.schema.json: names, parameters, types, defaults and required) fails the Python tests when a schema changes.
For a deliberate change, run cd server && UPDATE_TOOL_SCHEMAS=1 uv run --with pytest pytest tests/test_tool_schemas.py and commit the snapshot diff, so reviewers see the change.
A threaded fake HTTP server covers health, authentication, busy responses, job polling and PNG download.
Core tests cover parsing, serialization, formatting, units and query processing.
These tests do not require a live Revit model.
Tests inside Revit¶
tests/RevitModelMcp.Addin.Tests runs readers and action executors directly through Nice3point.TUnit.Revit.
A licensed Revit installation and an interactive Windows user session are required.
The solution lists the project under tests without a default build mapping; CI continues to run only Core tests.
RunRevitTests=true opts into Revit test discovery.
.\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'
-Model defaults to RMM_TEST_MODEL, then C:\Projects\rmm-test\2027\Snowdon Towers Sample Architectural.rvt.
Each test opens a private temporary copy and closes it without saving.
Actions commit inside an outer transaction group that teardown rolls back.
The tests compare summary text, the returned undo name, the distinct underlying action transaction name, and resulting model state.
The fixture uses RevitApiUiTest because the add-in assembly and executor reference RevitAPIUI.
The tests check the returned undo name and commit events; native undo menu inspection remains a manual check.
The runner refuses to start while any Revit.exe exists and requires at least 15 passing tests.
TRX files default to C:\Projects\rmm-test\out\tests\addin-<year>.trx; -OutputDirectory overrides the directory.
A missing model skips the tests when the executable runs directly; the script treats that condition as an error.
PR validation records the workstation years, TRX counters, and any failures.
See manual test plan and validation evidence.
Release ritual¶
Before merging the release PR, run the in-Revit regression command above for the oldest and newest supported years. Run the SingleUser installer smoke from an interactive Windows workstation with no running Revit:
.\build\release-smoke.ps1 -Msi '.\output\RevitModelMcp-1.0.0-SingleUser.msi' -ExpectedVersion '1.0.0'
-ExpectedVersion matches the DLL ProductVersion before its commit suffix; prerelease suffixes remain significant.
The smoke installs per user, checks manifests and DLL versions for installed Revit years, opens the test model in Revit 2027 through its own interactive scheduled task, waits for a fresh heartbeat, and calls revit_ping through pipe/1 using the Python server source and uv run.
-ServerDirectory selects the server checkout; -Model selects the 2027 test model.
An existing SingleUser MSI installation requires -PreviousMsi <full previous package> with the same ProductCode.
The smoke backs up that package and reinstalls it in its original scope before restoring the previous add-in files and settings.
The smoke closes only its own Revit process, removes only its own rmm-smoke-* task, uninstalls the package, and restores the previous per-year add-in from a retained backup.
The output directory contains MSI logs, the ping response, the backup, and a PASS/FAIL table with restored ProductVersions.
A failure returns exit code 1; cleanup failures remain visible in the table.
Attach the table and TRX counters to release validation before publication.
- Merge PRs with Conventional Commit titles.
- release-please maintains a
chore(main): release X.Y.ZPR with generated changelog entries and version updates. - Releases ship on Monday (Europe/Moscow). The maintainer merges the release PR on Monday after required checks pass.
- Check the Release please workflow, both MSI assets, six ZIPs, wheel, source distribution and
SHA256SUMS.txt. If a job fails withThe job was not acquired by Runner, that is a GitHub infrastructure failure: rerun it withgh run rerun <run id> --failed. - Check PyPI, MCP Registry and Smithery job results for stable releases. Check the weekly WinGet run separately; download its manifests if submission is not configured.
The same release checkout produces the GitHub assets and the Autodesk store bundle.
GitHub builds use the default Channel=github; ManagedInstall controls deployment separately.
After staging the GitHub assets, the workflow rebuilds the frozen server with python build/server-exe/build.py --channel store and all Revit years with -p:Channel=store.
It packs revit-model-mcp-<version>-store.bundle.zip with Bundle__Store=true, exports the store MCP manifest, and checks both against the bundled executable with build/store_check.py.
The release uploads the checked store ZIP and manifest as a separate artifact and includes them in its release assets and checksums.
The Store workflow also produces the ZIP and manifest for relevant pull requests or a manual run without publishing a release.
The maintainer downloads the final Windows store artifact and verifies installation, removal, batch start and fetch, policy enforcement, and window branding in Revit before submission. The submission uses the ZIP, its matching manifest, SHA256, current declaration, and screenshots from that exact build. The maintainer identifies the authoritative Autodesk submission record and submits the replacement through the Autodesk publisher portal. A successful local checker does not establish that Autodesk accepts the package or its export-tool scan. Release-please still owns all version and changelog changes. See Store packaging for the build and validation commands.
When the SIGNPATH_API_TOKEN secret and the SIGNPATH_ORGANIZATION_ID variable are set, the release job submits the MSIs and ZIPs to SignPath and waits up to 90 minutes for the approval of the signing request. Without them the job skips signing and publishes unsigned assets. Optional variables SIGNPATH_PROJECT_SLUG, SIGNPATH_SIGNING_POLICY_SLUG and SIGNPATH_ARTIFACT_CONFIGURATION_SLUG override the defaults revit-model-mcp, release-signing and release. The artifact configuration lives in .signpath/artifact-configuration.xml; keep the copy in SignPath identical.
The required release-window check keeps the release PR red outside Monday in Europe/Moscow time and passes at once for every other PR.
A scheduled run refreshes it when Monday starts and when it ends.
For an urgent fix, the maintainer adds the hotfix-release label to the release PR, which turns the check green and allows a release on another day.
The size of the change, not the calendar, decides the version bump: feat bumps the minor version and fix the patch version, as described below.
release-please owns CHANGELOG.md, the version in server/pyproject.toml and both versions in server/server.json.
Version bumps follow Conventional Commits. To force a major version, add a Release-As: X.Y.Z footer to a commit; see release-please-config.json. server/pyproject.toml remains the package version checked by the build.
The simple strategy skips its absent default version.txt; no separate version file is maintained.
Before 1.0, feat and breaking changes bump the minor version, while fix bumps the patch version.
Visible documentation and dependency changes can also produce a patch release.
The bump-patch-for-minor-pre-major option is false to retain minor bumps for features.
Merging the release PR creates vX.Y.Z and a GitHub Release with the generated notes.
The workflow calls the existing build and publish pipeline directly; tags created with GITHUB_TOKEN do not trigger tag-push workflows.
The pipeline appends an Install section and replaces only that section on retries.
A manually pushed tag also runs the pipeline and uses GitHub-generated notes if no release exists.
The tag version must equal server/pyproject.toml.
Release PRs created or updated with GITHUB_TOKEN require approval to run pull request workflows.
The maintainer selects Approve workflows to run in the release PR before waiting for required checks.
See GitHub workflow trigger rules.
Repository Actions settings must allow GitHub Actions to create pull requests.
Release assets¶
CI builds all six add-in years, uploads installable R22, R26 and R27 folder layouts, and an installers artifact with both MSIs covering every year.
It extracts both MSIs, rejects Revit API assemblies, and checks installation and removal for each built year.
CI also checks that each MSI refuses to install while the other scope is installed.
The release-please workflow or a manually pushed v<version> tag triggers all six add-in builds and Core/server tests.
The tag version must match server/pyproject.toml.
The GitHub Release contains six per-year ZIPs, single-user and multi-user MSIs, the Python wheel and source distribution, and SHA256SUMS.txt covering every asset.
Extract each year's ZIP into %APPDATA%\Autodesk\Revit\Addins\20<yy> while that Revit instance is closed.
The archive root contains RevitModelMcp.addin and the RevitModelMcp assembly directory.
The release workflow invokes the WixSharp pipeline with Build__Version set to the tag version.
CI reads the repository version from server/pyproject.toml and builds with <version>-dev, for example 0.9.1-dev, so the add-in reports 0.9.1-dev+<commit> and passes the server's same-major check.
A local dotnet build without -p:Version takes the plain version from server/pyproject.toml, so the add-in passes the same-major check against the server in the same checkout.
After building the required years on Windows, the same packaging command is available locally:
$env:Build__Version = '0.2.0'
dotnet run --project build -- pack --no-build
pack --no-build packages existing src/RevitModelMcp.Addin/bin/Release.R* directories without cleaning or recompiling them.
Use a fresh checkout for release packaging to exclude stale configurations.
The module installs WiX 7, accepts its EULA, installs the matching UI extension, and writes the MSIs to output/.
Single-user installation uses %APPDATA%\Autodesk\Revit\Addins\<year>.
Multi-user installation uses %ProgramData%\Autodesk\Revit\Addins\<year> through 2026 and %ProgramFiles%\Autodesk\Revit\Addins\2027 for 2027.
dotnet run --project build -- bundle builds the Revit .bundle zip from the same Release.R*/publish output, and python build/validate_bundle.py output/RevitModelMcp.bundle.zip --years 2022-2027 checks its PackageContents.xml and file layout on any platform.
CI runs both after the six Revit builds and uploads the zip as the revit-model-mcp-bundle artifact.
CI and the release workflow also rebuild the six years with -p:ManagedInstall=true, run the bundle step with Bundle__Managed=true and validate revit-model-mcp-<version>-managed.bundle.zip with --managed-version. The release attaches that zip.
Stable releases publish the wheel and source distribution to PyPI through GitHub OIDC in the pypi environment.
The pending publisher configuration is listed above the pypi job in .github/workflows/release.yml.
PyPI failure does not block the GitHub Release.
After PyPI succeeds, the workflow updates both versions in server/server.json and publishes to the official MCP Registry through GitHub OIDC.
Registry publishing is best-effort and its response appears in the job log.
Prerelease tags containing - skip PyPI and MCP Registry publishing.
release-please.yml dispatches release.yml for the new tag (a top-level run, which PyPI trusted publishing requires). To start the same pipeline by hand from the Actions tab, select the existing tag under "Use workflow from" and enter that same tag as the input.
The calling job grants id-token: write; the PyPI job retains the pypi environment and both publishers retain OIDC permissions.
WinGet runs weekly on Monday at 06:00 UTC for the latest published stable release, or manually with an optional stable release tag.
It waits without replacing an open package submission PR while moderation continues. A maintainer can manually select force_replace to replace older open PRs after the new PR is submitted.
Eligible runs generate and validate manifests for Sharafutdinov.RevitModelMcp and upload a winget-manifests artifact.
After successful submission, the workflow posts the Microsoft CLA agreement automatically.
Submission requires the optional WINGET_TOKEN repository secret, a classic PAT with public_repo and workflow scopes.
The workflow scope lets the workflow sync the token owner's winget-pkgs fork.
Without the token, eligible runs still generate and upload manifests.
On Windows, build/winget/New-WingetManifests.ps1 -Version 0.2.0 -ReleaseTag v0.2.0 -OutputDir artifacts/winget downloads the MSIs and reads their hashes and product codes.
-SkipDownload uses matching MSIs already in output/.
Contributions are licensed under the MIT license.