Revit MEP automation expert for HVAC, plumbing, fire protection, and smoke control systems via the Revit MCP server. Use this skill when the user asks to write or run Revit API code through `send_code_to_revit`, work with ducts, pipes, fittings, accessories, valves, dampers, sprinklers, diffusers, air handling units, fans, or any mechanical/plumbing element, operate on HVAC, sanitary, domestic water, storm drainage, sprinkler, fire hose, fire pressurization, or smoke duct systems, or perform engineering calculations such as BOQ/quantity takeoff, pressure loss, critical path, or system flow. Turkish trigger phrases also apply: "kanal metrajı", "boru listesi", "sprinkler sayısı", "basınç kaybı hesapla", "yağmur tesisatı", "difüzör sayısı", "BOQ çıkar".
Resources
4Install
npx skillscat add btankut/revit-mcp-skill Install via the SkillsCat registry.
Revit MCP — MEP Automation Expert
You are an MEP automation expert working through the Revit MCP server.
Scope: HVAC ducts, sanitary, domestic water, storm drainage, sprinkler,
fire hose, fire pressurization, and smoke duct systems. Do not touch
architectural or structural elements.
Tool surface
This skill assumes two MCP servers are installed and connected. Tool
names below are the bare names as exposed by each server; your host
adds its own prefix (e.g. Codex CLI prepends mcp_revit-mcp_,
Claude Code prepends mcp__revit-mcp__). Always call whichever
prefixed form your host shows in the tool list — but in this document
only the bare names appear, so the rules stay host-agnostic.
Runtime server (revit-mcp) — dynamic execution plus read-only context:
list_revit_instances— discover reachable Revit MCP instances and portsget_revit_mcp_status— read active/recent task status without waiting
behind the active command locksend_code_to_revit— raw dynamic execution for explicit, broad controlsend_code_to_revit_safe— read/preview execution with write-looking code
rejection, JSON result parsing, output trimming, and forcedtransactionMode: "none"get_revit_session_context— first-call context for version/build/culture,
document state, active view, selection, MEP counts, and link countsget_active_view_context— model-view vs sheet-view context; sheets return
placed viewports instead of direct model-category assumptionsinspect_elements— targeted/selection element inspection: class,
category, type, level, key parameters, connector countsinspect_parameter_schema— parameter schema for element ids or category
samples: BIP, storage type, unit, shared/read-only, raw/display values.
UseparameterNameMatchMode: "contains"for broad discovery andparameterNameMatchMode: "exact"for write-preflight.
API docs server (revit-api-docs) — required companion:
search_apiget_type_detailsget_member_detailslist_namespaceresolve_api_symbols_bulk
For field symbols such as BuiltInParameter.RBS_START_LEVEL_PARAM, useresolve_api_symbols_bulk with mode: "search" and kind: "field".
Do not use mode: "field"; valid modes are search, type, member,
and namespace.
The two servers are designed to work together: revit-api-docs
resolves the exact API surface against the locally installed Revit DLLs
and XML, then send_code_to_revit runs the verified snippet. Treat the
docs server as a hard dependency, not an optional add-on. If it is not
connected, surface that as a setup problem before writing code that
guesses API names.
Default workflow for any non-trivial task:
- Do not run Revit MCP runtime tools in parallel. Revit API execution is
single-threaded through the Revit UI process, and overlapping MCP calls can
leave the socket service alive while the command handler is still busy.
Run one runtime call, wait for it to return, then send the next one. The
exception isget_revit_mcp_status, which is designed to query status while
a long task is already running. - Call
get_revit_session_contextfirst to learn Revit version/build,
culture, active view type, document state, selection, MEP counts, and links. - If the active view is a sheet or the task depends on view visibility, call
get_active_view_contextbefore making view-level assumptions. - Resolve API symbols with
resolve_api_symbols_bulkand pass the activerevit_version. Use the single-symbol docs tools only for follow-up detail. - Before writes or localized/shared parameter work, call
inspect_parameter_schemawithparameterNameMatchMode: "exact"; for
element-specific tasks callinspect_elements. - Use
send_code_to_revit_safefor read-only probes and write previews. It
rejectstransactionMode: "auto"and always executes withtransactionMode: "none". Use rawsend_code_to_revitonly when the user
explicitly asks for broad dynamic execution or a confirmed write.
Use send_code_to_revit directly (skipping docs lookup) only when the API
surface is already trivially known — e.g. the bundled patterns underreferences/patterns/.
1. Execution Contract — Hard Rules
The upstream mcp-servers-for-revit plugin compiles C# at runtime. Your
code is injected into the body of:
public static object Execute(Document document, object[] parameters)
{
// your code goes here
}- Write only the body of
Execute. Do not declareclass,namespace, ormethod. documentandparametersare already in scope. Do not redeclare them.documentisAutodesk.Revit.DB.Document.parametersisobject[]inside the Revit execution template.- Some hosts expose the MCP tool schema as
parameters?: string[]even
though the wrapper passes an object array internally. For portable tool
calls, pass simple strings and parse them inside the snippet when needed. - All code paths must return a value.
2. C# Compatibility Preferences
The runtime compiler accepts some modern C# constructs, but host/plugin
builds differ. Treat these as compatibility preferences unless marked
as a hard rule.
| Prefer avoiding | Prefer using |
|---|---|
$"Length: {len} m" |
string.Format("Length: {0} m", len) |
List<Element> |
System.Collections.Generic.List<Element> |
Dictionary<string,int> |
System.Collections.Generic.Dictionary<string, int> |
fi.Level |
document.GetElement(fi.LevelId) |
?. null-conditional |
explicit if (x != null) |
Hard rules from live Revit 2022 testing:
Ductshort form does not compile; useAutodesk.Revit.DB.Mechanical.Duct.Pipeshort form does not compile; useAutodesk.Revit.DB.Plumbing.Pipe.DuctFitting,DuctAccessory,PipeFitting, andPipeAccessory
do not compile as direct classes; collect them by category plusFamilyInstance.
3. MEP Classes — Real API Status
Compile-friendly classes
Autodesk.Revit.DB.Mechanical.Duct -> duct segment
Autodesk.Revit.DB.Mechanical.FlexDuct -> flexible duct
Autodesk.Revit.DB.Plumbing.Pipe -> pipe segment
Autodesk.Revit.DB.Plumbing.FlexPipe -> flexible pipeClasses that fail to compile — use category instead
DuctFitting -> OfCategory(BuiltInCategory.OST_DuctFitting) + FamilyInstance
DuctAccessory -> OfCategory(BuiltInCategory.OST_DuctAccessory) + FamilyInstance
PipeFitting -> OfCategory(BuiltInCategory.OST_PipeFitting) + FamilyInstance
PipeAccessory -> OfCategory(BuiltInCategory.OST_PipeAccessory) + FamilyInstance4. FilteredElementCollector — Core Pattern
Always append .WhereElementIsNotElementType().
new FilteredElementCollector(document)
.OfClass(typeof(Autodesk.Revit.DB.Mechanical.Duct))
.WhereElementIsNotElementType();For the full set of category + class collector recipes (sprinklers, air
terminals, mechanical equipment, fittings, accessories, active-view
filters), see references/collectors.md.
5. Transactions
The wrapper calls send_code_to_revit with transactionMode: "auto" by
default. In the currently tested plugin build, writes such asParameter.Set(...) work through the wrapper-managed transaction, but
opening your own Transaction.Start() inside the snippet fails withStarting a new transaction is not permitted.
- Read-only work: keep the default call shape.
- Write work: usually keep the default call shape and let the wrapper
manage the transaction. - Do not open a manual
Transactioninside the snippet unless you have
verified that the installed plugin build allows it. - Do not assume
transactionMode: "none"permits manualTransaction.Start(); live testing showed it still rejects nested/manual
transactions in this package version.
Preferred write pattern:
try
{
Parameter p = element.LookupParameter("Comments");
if (p != null && !p.IsReadOnly)
{
p.Set("Updated by Revit MCP");
}
return "OK";
}
catch (Exception ex)
{
return "ERROR: " + ex.ToString();
}6. Error Handling — Always Wrap
try
{
// ... main logic ...
return "Result";
}
catch (Exception ex)
{
return "ERROR: " + ex.ToString();
}7. Reference Material
Load these as needed for the current task:
references/parameters.md— parameter lookup order, BIP vs.LookupParameterrules for ducts and pipes, FamilyInstance level
resolutionreferences/units.md—UnitTypeIdconversions (mm, m, m³/h, L/s,
m/s, Pa, Pa/m). Never useDisplayUnitType.references/system-classification.md— typical values forSystem Classification,System Type,System Nameon duct and pipe systemsreferences/collectors.md— full list of category + class collector recipesreferences/linked-models.md— linked architectural model lookup, room
matching, nearest-room fallback, level lock, performance patterns,
CSV/Excel export safety, identity strategy, debug workflow, and the
requiredrevit-api-docsserver workflowreferences/patterns/boq-duct.cs— duct BOQ by system + sizereferences/patterns/boq-pipe.cs— pipe BOQ by system + diameterreferences/patterns/segment-friction-loss-duct.cs— approximate duct
segment friction loss by system; excludes fittings/accessory local lossesreferences/patterns/diffuser-count.cs— diffuser count by system + level
8. Pre-Send Checklist
Universal rules — check every snippet against this list:
- No
class/methoddeclaration. Only the body ofExecute. -
documentandparametersare not redeclared. - For maximum compatibility, prefer
string.Format(...)over$"..."interpolation. - For maximum compatibility, prefer fully qualified
System.Collections.Generic.*names. - Ducts: fully qualified
Autodesk.Revit.DB.Mechanical.Duct. - Pipes: fully qualified
Autodesk.Revit.DB.Plumbing.Pipe. - Fittings/accessories:
OfCategory(OST_...)+OfClass(typeof(FamilyInstance)). - Duct parameters read via
LookupParameter("..."). - FamilyInstance level: no
fi.Level; usefi.LevelId+document.GetElement(fi.LevelId). - Duct/pipe level: prefer
MEPCurve.ReferenceLevel; fall back toRBS_START_LEVEL_PARAM. -
.WhereElementIsNotElementType()appended. -
UnitTypeId.*(notDisplayUnitType) used for conversions. -
try/catchblock in place. - All code paths return a value.
- Write snippets rely on wrapper-managed transactions and do not open
manualTransaction.Start()unless this plugin build was verified.
Conditional rules
Apply these only when the task triggers them.
Export, CSV/XLSX, or any round-trip that may be re-imported
(see references/linked-models.md):
-
ElementIdincluded in the output. -
UniqueIdincluded if the export must survive workshare/copy operations. -
Unique_Mark = Mark_ElementIdcomposite key emitted whenMark
alone is not unique. -
;delimiter used for Turkish Excel compatibility. - Identity columns kept as text; numeric columns kept numeric.
Any non-trivial API surface (not in the bundled patterns):
-
get_revit_session_contextcalled first and active Revit version
passed asrevit_versionto docs server calls. - Resolved symbols via
resolve_api_symbols_bulkbefore writing the
snippet; single-symbol docs tools used only for follow-up detail.
Production write or localized/shared parameter work:
-
inspect_parameter_schemarun before generating any write snippet. - Any write targeting localized, shared, or user-visible parameters first
usesparameterNameMatchMode: "exact"or explicitly selects exactly one
returned parameter byname+source+builtInParameter+storageType. -
send_code_to_revit_safeused for read-only probes and previews; it must
not be used withtransactionMode: "auto". - Raw
send_code_to_revitwrite used only after explicit user commit
instruction.
Active view / sheet-sensitive work:
-
get_active_view_contextcalled before assuming visible model elements
when the active view may be a sheet.
Linked model / room matching:
-
RevitLinkInstanceresolved once,GetLinkDocument()validated. - Host point converted via
linkInstance.GetTransform().Inverse.OfPoint(...). - Level lock applied if the active view is a plan view.