
natlabrockies/openstudio-mcp
Developer toolsIt lets MCP hosts — Claude Desktop, Claude Code, Cursor, VS Code — create, query, and modify OpenStudio models, run EnergyPlus, and read results, all in plain language.
개요
It lets MCP hosts — Claude Desktop, Claude Code, Cursor, VS Code — create, query, and modify OpenStudio models, run EnergyPlus, and read results, all in plain language. The server handles the OpenStudio/EnergyPlus complexity behind MCP tool calls. - "Create a 10-zone office with VAV reheat and run an annual simulation." - "What's the EUI? Show me the unmet heating hours." - "Switch the HVAC from VAV to VRF heat pumps and compare energy use." - "Add R-30 roof insulation and see how it affects the cooling load." - "Build two adjacent zones from floor plans, match the shared wall, add 40% south glazing." - "Write a measure that sets all lights to 8 W/m², test it, apply it, and compare the EUI." - "Submit this OpenStudio analysis JSON to my server, wait for completion, and download the results" - "Apply the AEDG Small Office measure from my local measures directory" The AI picks the right tools, calls them in sequence, and summarizes — no scripting.
README
OpenStudio®-MCP SWR 26-035
Model Context Protocol server for OpenStudio building energy simulation. It lets MCP hosts — Claude Desktop, Claude Code, Codex, VS Code — create, query, and modify OpenStudio models, run EnergyPlus, and read results, all in plain language. The server handles the OpenStudio/EnergyPlus complexity behind MCP tool calls.
150+ tools · bundled workflow skills · 500+ integration tests
Published in SoftwareX — see Cite this work.
Contents
- What you can ask for
- Quick start (local)
- Remote & multi-user (HTTP)
- Security and simulation sandbox
- Skills & Tools
- Tool reference
- Reference
- Examples
- Testing
- Linting and formatting
- Architecture
- Cite this work
- License
What you can ask for
- “Create a 10-zone office with VAV reheat and run an annual simulation.”
- “What’s the EUI? Show me the unmet heating hours.”
- “Switch the HVAC from VAV to VRF heat pumps and compare energy use.”
- “Add R-30 roof insulation and see how it affects the cooling load.”
- “Build two adjacent zones from floor plans, match the shared wall, add 40% south glazing.”
- “Write a measure that sets all lights to 8 W/m², test it, apply it, and compare the EUI.”
- “Submit this OpenStudio analysis JSON to my server, wait for completion, and download the results”
- “Apply the AEDG Small Office measure from my local measures directory”
The AI picks the right tools, calls them in sequence, and summarizes — no scripting.
Quick start (local)
Runs the server locally over stdio — one container per user, launched by your MCP host. For a shared deployment, see Remote & multi-user.
Prerequisites: Docker Desktop running, and an MCP host (Claude Desktop is the easiest start).
1. Get the image
Option A: Pull from Docker Hub (no checkout needed)
docker pull nrel/openstudio-mcp:v1.2.1
Tags on nrel/openstudio-mcp:
| Tag | What it is |
|---|---|
vX.Y.Z (e.g. v1.2.1) |
A tagged release. Pin one of these for reproducible work |
dev |
Latest develop branch build; moves on every merge |
Every tag is a multi-arch manifest (amd64 + arm64), so the same docker pull works on Intel/AMD and Apple Silicon.
Option B: Build locally (for development, or to run unreleased changes)
git clone https://github.com/NatLabRockies/openstudio-mcp.git
cd openstudio-mcp
| Machine | Build command |
|---|---|
| Intel/AMD (Linux, Windows, Intel Mac) | docker build -t openstudio-mcp:dev -f docker/Dockerfile . |
| Apple Silicon (M-series) | docker build --platform linux/arm64 -t openstudio-mcp:dev -f docker/Dockerfile.arm64 . |
Both produce a local image tagged openstudio-mcp:dev. The arm64 Dockerfile builds natively from NREL’s arm64 .deb (the upstream nrel/openstudio base is amd64-only, so plain Dockerfile runs under slow emulation on Apple Silicon).
Remember your image name. Step 2 uses openstudio-mcp:dev (the local build). If you pulled instead, substitute nrel/openstudio-mcp:v1.2.1 (or whichever tag you pulled) wherever openstudio-mcp:dev appears.
2. Configure your host
Pick three host folders first: one with your models (mounted read-only at /inputs), one for simulation output (/runs), and one for measures you author or download (/measures). Create them if they don’t exist. In the snippets below they are /path/to/models, /path/to/runs, and /path/to/measures; replace them with absolute paths.
Option A: Claude Desktop (JSON)
Add to your Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json on macOS, %APPDATA%\Claude\claude_desktop_config.json on Windows), then restart Claude Desktop.
{
"mcpServers": {
"openstudio-mcp": {
"command": "docker",
"args": [
"run", "--rm", "-i",
"-v", "/path/to/models:/inputs:ro",
"-v", "/path/to/runs:/runs",
"-v", "/path/to/measures:/measures",
"-e", "OPENSTUDIO_MCP_MODE=prod",
"openstudio-mcp:dev", "openstudio-mcp"
]
}
}
}
Option B: Codex & other clients (TOML)
Add to your client config (e.g., ~/.codex/config.toml on macOS/Linux, %USERPROFILE%\.codex\config.toml on Windows).
[mcp_servers.openstudio-mcp]
command = "docker"
startup_timeout_sec = 120
args = [
"run", "--rm", "-i",
"-v", "/path/to/models:/inputs:ro",
"-v", "/path/to/runs:/runs",
"-v", "/path/to/measures:/measures",
"-e", "OPENSTUDIO_MCP_MODE=prod",
"openstudio-mcp:dev", "openstudio-mcp"
]
Image name: the second-to-last argument (openstudio-mcp:dev) must match the image from step 1. If you pulled from Docker Hub, change it to nrel/openstudio-mcp:v1.2.1 (or the tag you pulled). Otherwise Docker fails with Unable to find image 'openstudio-mcp:dev'.
Mounts: each -v host:container line shares a host folder with the container. The container is discarded on exit (--rm), so anything you want to keep must live on a mount:
| Container path | Host folder | Why |
|---|---|---|
/inputs (read-only) |
your models, weather files, gbXML, error files | The AI can only open files it can see. Put a file here and refer to it as /inputs/ in your prompt |
/runs |
simulation output | Every run writes to /runs// (OSM, SQL, HTML reports). Without the mount, results vanish when the container exits |
/measures |
authored + BCL measures | create_measure and BCL downloads land under /measures//{custom,bcl}. Mount it so measures survive restarts; list_local_measures discovers them |
Use absolute paths. On Windows, use forward slashes in Docker args:
"-v", "C:/Users/you/models:/inputs:ro",
"-v", "C:/Users/you/openstudio-runs:/runs",
"-v", "C:/Users/you/openstudio-measures:/measures",
Restart your client after saving the config file.
3. Verify and chat
Open your host’s tools menu (in Claude Desktop, the tools/connectors button under the prompt box) and confirm the openstudio-mcp server is listed with its tools. Then try these prompts in order of complexity:
Simple: “Create an example model and tell me about it”
Medium: “Create a baseline office with ASHRAE System 3 and show me the HVAC components”
Advanced: “Load my model at /inputs/MyBuilding.osm, apply the 90.1-2019 typical building template, and run a simulation”
The AI reads your prompt, picks the right tools from the 150+ available, calls them in sequence, and summarizes the results, no scripting required.
Working with Your Own Files
Place files in the host folder mapped to /inputs (see step 2) rather than uploading them (drag-and-drop or attach) through the web or desktop chat interface. Uploads never reach the MCP tools; mounted files do.
# Example: analyzing an EnergyPlus error file in an AI-chat session
# 1. Copy to the host folder mounted at /inputs
cp eplusout.err /path/to/models/
# 2. Reference by MCP path in your prompt
"Analyze the warnings in /inputs/eplusout.err and create a measure to fix them"
Why not upload? Attaching a file to the chat (drag-and-drop, the paperclip button, or paste) only gives the AI the file’s contents in the conversation. It never lands on disk inside the Docker container, so MCP tools like load_osm_model can’t open it, and the AI may fall back to writing scripts instead of using the 150+ MCP tools. A file in the /inputs mount is on disk where the tools can read it. Simulation outputs (results, SQL, HTML reports) are already under /runs and need no copying.
Client Compatibility
Any MCP host can launch the same docker run command. See the MCP documentation for host-specific config locations (VS Code Copilot, Claude Code, Codex, Windsurf, Gemini CLI).
| Client | Status | Notes |
|---|---|---|
| Claude Desktop | Full support | All tools available |
| Claude Code | Full support | ToolSearch auto-defers tools for efficient discovery |
| Codex | Compatible | MCP client via config |
| VS Code Copilot | Compatible | MCP support via config |
| Windsurf | Compatible | Host tool cap is ~100; use includeTools/excludeTools to limit the exposed tool set |
| Gemini CLI | Compatible | Use includeTools/excludeTools if needed |
| OpenAI API | Compatible | Use defer_loading for best results |
Remote & multi-user (HTTP)
The quick start runs one container per user over stdio. To host it on one machine and let teammates connect from their own laptops — each with an isolated session, run directory, and optional bearer-token or JWT auth — run it over streamable HTTP (-e MCP_TRANSPORT=http). Works with Claude Code, VS Code, and any host that supports streamable-HTTP MCP servers.
Since a remote server can’t see files on your laptop, the file_transfer tools (request_upload / get_upload / request_download) move models, weather files, and measure .zips in and out over a signed, out-of-band channel — see docs/remote-multi-user.md §6.
See docs/remote-multi-user.md for setup, auth, the isolation model, and log access — and docs/run-retention.md for optional disk garbage-collection.
Security and simulation sandbox
OpenStudio measures are Ruby or Python programs and must be treated as untrusted
code. EnergyPlus workflows can also invoke measure code. Docker isolates the
container from the host, while openstudio-mcp adds a second sandbox around the
child processes used by apply_measure, test_measure, measure metadata
refresh, and simulations.
The default OSMCP_SANDBOX=auto mode provides the following controls on Linux,
including Docker Desktop’s Linux VM:
- Privilege drop: child processes run as the image’s unprivileged
sandboxuser (UID/GID 1001), while the MCP server retains only the privileges needed to prepare run directories. - Filesystem policy: Landlock denies filesystem access by default. The
current run or staged measure directory is writable; required system and
OpenStudio directories are read-only.
/repo,/inputs, and other users’ run directories are not exposed to measure code. - Network policy: seccomp denies outbound IP networking by default.
- Secret isolation: child processes receive an allowlisted environment instead of inheriting API keys, tokens, and other server environment variables.
- Process hardening:
no_new_privsprevents privilege recovery through setuid executables. Resource limits constrain generated file size and process count, and simulations have a wall-clock timeout. - Staging: input models, weather files, measures, and OSWs are copied into a private run directory before execution. Escaping symlinks are rejected.
On Linux, auto fails closed if Landlock or the seccomp network filter cannot
be installed: the untrusted child process does not run. On native macOS or
Windows without Docker, kernel confinement is unavailable and the server warns
that only environment filtering is active. Use the Docker image when running
untrusted measures.
Sandbox options
| Variable | Default | Behavior |
|---|---|---|
OSMCP_SANDBOX |
auto |
auto, full, and landlock request environment filtering, UID/GID drop, resource limits, Landlock filesystem confinement, and seccomp network denial. posix omits Landlock and seccomp. off disables child-process confinement and is only appropriate for trusted local code. Unknown values fall back to auto. |
OSMCP_SANDBOX_NET |
deny |
deny blocks outbound IP networking. allow permits it for trusted measures that must fetch external resources. |
OSMCP_SANDBOX_UID / OSMCP_SANDBOX_GID |
1001 |
Account used for confined child processes. Values less than 1 are rejected. The default matches the sandbox user built into the image. |
OSMCP_SANDBOX_RLIMIT_FSIZE |
10737418240 |
Maximum size in bytes of one file created by a child process. 0 disables this limit. |
OSMCP_SANDBOX_RLIMIT_NPROC |
1024 |
Maximum processes/threads for the sandbox UID. 0 disables this limit. |
OSMCP_SANDBOX_RLIMIT_NOFILE |
0 |
Optional open-file descriptor limit. |
OSMCP_SANDBOX_RLIMIT_CPU |
0 |
Optional CPU-seconds limit. Disabled by default because annual simulations can be long-running. |
OSMCP_SANDBOX_RLIMIT_AS |
0 |
Optional virtual-memory limit. Prefer Docker memory limits because restrictive address-space limits can break EnergyPlus. |
OSMCP_SIM_TIMEOUT_SECONDS |
7200 |
Wall-clock timeout for a simulation. 0 disables the timeout. |
OSMCP_ALLOW_UNSANDBOXED |
unset | In HTTP mode the server refuses to start (exit 3) unless the full tier is configured (OSMCP_SANDBOX=auto, OSMCP_SANDBOX_NET=deny, Linux). true overrides with a logged warning. |
OSMCP_ENABLE_CODE_TOOLS |
true |
Set false to not register the code-authoring tools (create_measure, edit_measure, create_python_plugin, edit_python_plugin, install_plugin_packages) on a deployment that must not let agents write code. |
OSMCP_TOOLS_ALLOW |
unset | Comma list; when set, only these tools are registered. Unknown names refuse startup. |
OSMCP_TOOLS_DENY |
unset | Comma list of tools to hide (deny wins over allow). Unknown names refuse startup. |
Secure deployment guidance
- Mount
/inputsread-only:-v /host/inputs:/inputs:ro. - Mount only the output directory at
/runs; any process allowed to write/runscan modify that host directory by design. - Do not mount the repository, home directory, Docker socket, credentials, or
broad host paths into production containers. The
/reposource mount in the testing commands is for development only. - Keep
OSMCP_SANDBOX=autoandOSMCP_SANDBOX_NET=denyfor untrusted measure authoring. Docker’s--network nonecan provide an additional container-wide network boundary when remote access is not required. - Use Docker CPU, memory, PID, and disk quotas as outer limits. The in-process resource limits are defense in depth, not replacements for container limits.
The sandbox protects the host and other run directories, but it intentionally allows measure code to modify its own staged run directory. Treat resulting OSM, SQL, report, and log files as untrusted outputs.
Skills & Tools (150+ total)
In Claude Code, the bundled skills add workflow automation and domain knowledge:
| Skill | Type | What it does |
|---|---|---|
/simulate |
Workflow | one-command simulate + results extraction |
/energy-report |
Workflow | comprehensive multi-category energy report |
/new-building |
Workflow | full model creation from scratch |
/retrofit |
Workflow | before/after ECM analysis |
/add-hvac |
Task | guided HVAC system selection |
/qaqc |
Task | pre-simulation model quality check |
/view |
Task | quick 3D model visualization |
/troubleshoot |
Task | diagnose simulation failures |
measure-authoring |
Knowledge | measure creation, SDK verification, wiring patterns |
ashrae-baseline-guide |
Knowledge | ASHRAE 90.1 system selection |
openstudio-patterns |
Knowledge | tool dependencies and model relationships |
tool-workflows |
Knowledge | multi-tool recipes for common operations |
/attribute-space-types |
Task | attribute standards space types to conditioned spaces (post-gbXML) |
/gbxml-import |
Task | Revit gbXML import + geometry-defect repair workflow |
/osaf-analysis |
Task | OpenStudio Analysis Framework workflow: algorithm selection, validation, submission |
/python-ems |
Task | custom EnergyPlus Python Plugin control/reporting logic (EMS) |
/file-transfer |
Task | move files to/from a remote server (signed upload/download URLs) |
Workflow/task skills are invoked with /name; knowledge skills load automatically. Any MCP host can also discover these guides via the list_skills() and get_skill(name) tools (baked into the image at /skills).
Tool reference
150+ tools, grouped by area — expand a group to see its tools. New here? create_new_building, run_simulation, and extract_summary_metrics cover most workflows; list_skills() and recommend_tools(task) help the AI find the rest.
Reference
ASHRAE baseline systems
All 10 ASHRAE 90.1 Appendix G baseline systems via add_baseline_system, plus modern templates DOAS, VRF, Radiant.
| # | Type | Description |
|---|---|---|
| 1 | PTAC | Packaged terminal AC (zone-level) |
| 2 | PTHP | Packaged terminal heat pump (zone-level) |
| 3 | PSZ-AC | Packaged single-zone rooftop AC |
| 4 | PSZ-HP | Packaged single-zone heat pump |
| 5 | Packaged VAV w/ Reheat | VAV with hot-water reheat |
| 6 | Packaged VAV w/ PFP Boxes | VAV with parallel fan-powered boxes |
| 7 | VAV w/ Reheat | Central VAV, chiller + boiler + tower |
| 8 | VAV w/ PFP Boxes | Central VAV, parallel fan-powered terminals |
| 9 | Gas Unit Heater | Heating-only (warehouses, garages) |
| 10 | Electric Unit Heater | Heating-only, electric |
HVAC component types
The component-properties tools query/modify these 15 types:
| Category | Components |
|---|---|
| Coils | CoilHeatingGas, CoilHeatingElectric, CoilHeatingWater, CoilCoolingWater, CoilCoolingDXSingleSpeed, CoilCoolingDXTwoSpeed, CoilHeatingDXSingleSpeed |
| Plant | BoilerHotWater, ChillerElectricEIR, CoolingTowerSingleSpeed |
| Fans | FanConstantVolume, FanVariableVolume, FanOnOff |
| Pumps | PumpConstantSpeed, PumpVariableSpeed |
Examples
25 worked examples with full tool-call sequences:
Testing
Full guide — framework, annotated examples, CI shards, writing tests — in docs/testing/.
# Unit tests (no Docker)
pytest tests/test_skill_registration.py -v
# Integration tests (Docker)
docker build -t openstudio-mcp:dev -f docker/Dockerfile .
docker run --rm -v "$PWD:/repo" -v "$PWD/runs:/runs" \
-e RUN_OPENSTUDIO_INTEGRATION=1 -e MCP_SERVER_CMD=openstudio-mcp \
openstudio-mcp:dev bash -lc 'cd /repo && pytest -vv -s tests/'
Linting and formatting (uv + pre-commit)
CI runs the same pre-commit hooks (.github/workflows/format_and_lint.yml).
# Install dev dependencies with uv
uv pip install -e ".[dev]"
# Run the pre-commit hooks locally
uv run pre-commit run --all-files
Architecture
- Transport: stdio (default) or streamable HTTP for remote/multi-user
- Protocol: MCP (JSON-RPC); in stdio prod mode, stdout is reserved for JSON-RPC and logs go to stderr
- Skills: 30+ skill modules under
mcp_server/skills//, each withtools.py(MCP registration) +operations.py(business logic); they auto-register - State: per-session in-memory model via
model_manager; runs under/runs//(or/runs///in HTTP mode)
Set OPENSTUDIO_MCP_MODE=prod for MCP hosts (quiet logs, no banner). Full system diagram, security analysis, and hardening notes: docs/architecture.md.
Contributing
Cite this work
If you use OpenStudio-MCP in research, please cite the SoftwareX article:
Ball, B.L., Long, N., Fleming, K., Goldwasser, D., 2026. OpenStudio-MCP: a model context protocol (MCP) server for AI agent-driven building energy modeling with the OpenStudio SDK. SoftwareX 36, 103020. https://doi.org/10.1016/j.softx.2026.103020
@article{ball2026openstudiomcp,
title = {{OpenStudio-MCP}: a model context protocol ({MCP}) server for {AI} agent-driven building energy modeling with the {OpenStudio} {SDK}},
author = {Ball, Brian L. and Long, Nicholas and Fleming, Katherine and Goldwasser, David},
journal = {SoftwareX},
volume = {36},
pages = {103020},
year = {2026},
issn = {2352-7110},
doi = {10.1016/j.softx.2026.103020}
}
License
See LICENSE.
설치
docker run --rm -i -v /path/to/models:/inputs:ro -v /path/to/runs:/runs -v /path/to/measures:/measures -e OPENSTUDIO_MCP_MODE=prod openstudio-mcp:dev openstudio-mcp설정
{
"mcpServers": {
"openstudio-mcp": {
"command": "docker",
"args": [
"run", "--rm", "-i",
"-v", "/path/to/models:/inputs:ro",
"-v", "/path/to/runs:/runs",
"-v", "/path/to/measures:/measures",
"-e", "OPENSTUDIO_MCP_MODE=prod",
"openstudio-mcp:dev", "openstudio-mcp"
]
}
}
}