mcp server
UBEM-MCP
Secure open-source MCP server for urban building energy modeling (UBEM): lets AI agents call fast energy models and validated, schema-aligned energy data at city scale.
Set up
Runs on your computer. Pick your agent and operating system.
This project does not publish an install method we can turn into a config. Follow the README instead.
README
synced Oct 22, 2026UBEM-MCP
A Model Context Protocol (MCP) server for urban building energy modeling (UBEM), built on EnergyPlus. It provides 41 tools for loading, validating, modifying, analyzing, and simulating EnergyPlus building energy models — including batch simulation of a whole portfolio of buildings at once, which is the core addition over single-model EnergyPlus servers.
Version: 0.1.0 EnergyPlus Compatibility: 26.1.0 (default; see Building against a different EnergyPlus version) Python: 3.10+
This project is derived from EnergyPlus-MCP (LBNL) — see NOTICE.md for attribution and the citation for the original work.
- Overview
- Installation
- Available Tools
- Usage Examples
- Batch Simulation
- Architecture
- Configuration
- Troubleshooting
- Contributing
- Cite this work
- License
Overview
UBEM-MCP makes EnergyPlus building energy simulation — for a single building or an entire portfolio — accessible to AI assistants and automation tools through the Model Context Protocol.
Key Features:
- 🏙️ Batch / Portfolio Simulation: Run many EnergyPlus models concurrently as a background job; poll for status and results instead of blocking on a single long-running call
- 🏗️ Complete Model Lifecycle: Load, validate, analyze, modify, and simulate IDF files
- 🔍 Deep Building Analysis: Extract detailed information about zones, surfaces, materials, and schedules
- 🔧 HVAC Intelligence: Discover, analyze, and visualize HVAC system topology
- 📈 Smart Output Management: Auto-discover and configure output variables/meters
- 📊 Advanced Visualization: Create interactive plots and HVAC system diagrams
Installation
Using the MCP Server
Prerequisites (all clients):
- Docker Desktop (macOS / Windows) or Docker Engine (Linux), running
giton your PATH- The
ubem-mcp-devimage built locally (step 1 below — do this once)
Choose the appropriate setup for your AI assistant or IDE:
Claude Desktop
-
Build the Docker image (one-time setup):
git clone https://github.com/TensorBuildLab/UBEM-MCP.git cd UBEM-MCP docker build -t ubem-mcp-dev -f .devcontainer/Dockerfile .devcontainer -
Locate the Claude Desktop config file for your OS:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json - Linux: Claude Desktop is not officially supported on Linux. If you use a community build, check its docs for the config path (commonly
~/.config/Claude/claude_desktop_config.json).
Create the file if it does not exist, then add:
{ "mcpServers": { "ubem": { "command": "docker", "args": [ "run", "--rm", "-i", "-v", "/path/to/UBEM-MCP:/workspace", "-w", "/workspace/ubem-mcp-server", "ubem-mcp-dev", "uv", "run", "python", "-m", "ubem_mcp_server.server" ] } } }Important:
- Replace
/path/to/UBEM-MCPwith the absolute path to your cloned repo.- macOS/Linux example:
/Users/yourname/code/UBEM-MCP - Windows example:
C:\\Users\\yourname\\code\\UBEM-MCP(use double backslashes in JSON)
- macOS/Linux example:
- Remove all comments (text after
//) when adding to the actual config file, as JSON doesn't support comments.
- macOS:
-
Restart Claude Desktop. The
ubemMCP server should appear in the MCP servers panel. -
Verify: in a new chat, ask "List the UBEM-MCP tools you have access to." You should see tools like
load_idf_model,run_batch_simulation,get_server_status. If not, check Troubleshooting.
VS Code
VS Code 1.102+ ships native MCP support. Config goes in .vscode/mcp.json at the workspace root (or in user settings under "mcp").
-
Build the Docker image (same as Claude Desktop step 1 above).
-
Create
.vscode/mcp.jsonin your project:{ "servers": { "ubem": { "command": "docker", "args": [ "run", "--rm", "-i", "-v", "${workspaceFolder}:/workspace", "-w", "/workspace/ubem-mcp-server", "ubem-mcp-dev", "uv", "run", "python", "-m", "ubem_mcp_server.server" ] } } }Important: Remove all comments (text after
//) when saving — JSON does not support comments. -
Reload VS Code (
Ctrl/Cmd+Shift+P→ Developer: Reload Window). Open the Chat view and confirm theubemMCP server shows as Running. -
Verify: ask the chat "What UBEM-MCP tools are available?" — you should see the tool list.
Cursor
-
Build the Docker image (same as Claude Desktop step 1 above).
-
Locate the Cursor MCP config file for your OS:
- macOS/Linux:
~/.cursor/mcp.json - Windows:
%USERPROFILE%\.cursor\mcp.json
Create the file if it does not exist, then add:
{ "mcpServers": { "ubem": { "command": "docker", "args": [ "run", "--rm", "-i", "-v", "/path/to/UBEM-MCP:/workspace", "-w", "/workspace/ubem-mcp-server", "ubem-mcp-dev", "uv", "run", "python", "-m", "ubem_mcp_server.server" ] } } }Important:
- Replace
/path/to/UBEM-MCPwith the absolute path to your cloned repo (Windows users: use double backslashes in JSON, e.g.C:\\Users\\yourname\\code\\UBEM-MCP). - Remove all comments (text after
//) when saving — JSON does not support comments.
- macOS/Linux:
-
Restart Cursor. Open Settings → MCP and confirm the
ubemserver is listed as connected. -
Verify: ask Cursor chat "What UBEM-MCP tools are available?" — you should see the tool list.
Development Setup
For contributors who want to modify or extend the MCP server:
VS Code Dev Container
The easiest development setup with all dependencies pre-configured.
Prerequisites:
Steps:
-
Clone and open in VS Code:
git clone https://github.com/TensorBuildLab/UBEM-MCP.git cd UBEM-MCP code . -
Click "Reopen in Container" when prompted (or press
Ctrl+Shift+P→ "Dev Containers: Reopen in Container") -
The container automatically installs EnergyPlus 26.1.0 and all dependencies (to pin a different version, see Building against a different EnergyPlus version)
Docker Setup
For direct Docker development without VS Code:
# Clone repository
git clone https://github.com/TensorBuildLab/UBEM-MCP.git
cd UBEM-MCP
# Build container
docker build -t ubem-mcp-dev -f .devcontainer/Dockerfile .devcontainer
# Run container
docker run -it --rm -v "$(pwd)":/workspace -w /workspace/ubem-mcp-server ubem-mcp-dev bash
# Inside container, install dependencies
uv sync --extra dev
Local Development
For local development (requires EnergyPlus installation):
Prerequisites:
- Python 3.10+
- uv package manager
- EnergyPlus 26.1.0 (or pin a different version — see Building against a different EnergyPlus version)
# Clone and install
git clone https://github.com/TensorBuildLab/UBEM-MCP.git
cd UBEM-MCP/ubem-mcp-server
uv sync --extra dev
# Run server for testing
uv run python -m ubem_mcp_server.server
Streamable HTTP Transport (Local Testing)
By default the server runs over stdio, which is what every MCP client config in this README uses. The server can also run as a token-authenticated streamable HTTP service — useful for testing remote-style deployments, smoke-testing with curl, or connecting clients that expect an HTTP MCP endpoint.
Prerequisites — pick one path:
- Docker (recommended; no local EnergyPlus install needed): build the
ubem-mcp-devimage once (per Docker Setup above). The image ships with EnergyPlus 26.1.0 and all Python deps pre-installed. - Local Development: follow Local Development above — Python 3.10+,
uv, and a local EnergyPlus install. HTTP mode pulls inuvicornandpython-dotenv, which are declared inpyproject.toml—uv sync --extra devwill install them.
1. Generate a bearer token. Tokens must be at least 32 characters:
openssl rand -hex 32
2. Create .env in ubem-mcp-server/ (gitignored). Copy from .env.example and fill in your values:
# EPLUS_IDD_PATH — ONLY set this for the Local variant.
# Leave it commented out / unset when using the Docker variant; the image has
# EnergyPlus 26.1.0 baked in and auto-detects it.
# EPLUS_IDD_PATH=/Applications/EnergyPlus-26-1-0/Energy+.idd
MCP_TRANSPORT=streamable-http
MCP_HTTP_HOST=0.0.0.0
MCP_HTTP_PORT=8000
MCP_HTTP_PATH=/mcp
# JSON array. Required when MCP_TRANSPORT=streamable-http.
MCP_TOKENS=[{"label":"local-dev","token":"<paste-32+-char-hex-here>"}]
3. Start the server. Pick the variant that matches your setup:
Docker (recommended): publish the port, mount the repo, and let the container pick up .env via --env-file. Run from the repo root:
docker run --rm \
-p 8000:8000 \
-v "$(pwd)":/workspace \
-w /workspace/ubem-mcp-server \
--env-file ubem-mcp-server/.env \
ubem-mcp-dev \
uv run python -m ubem_mcp_server.server
Local: (requires a host EnergyPlus install matching whatever EPLUS_IDD_PATH is set to in .env)
cd ubem-mcp-server
uv run python -m ubem_mcp_server.server
Either way you should see a log line like Listening on http://0.0.0.0:8000 (path=/mcp, 1 tokens).
4. Smoke-test with curl. The server exposes two endpoints:
# Health check — no auth required
curl -s http://localhost:8000/health
# Unauthenticated MCP request — should return 401
curl -i -X POST http://localhost:8000/mcp
# Authenticated initialize handshake
TOKEN=<paste-your-token>
curl -i -X POST http://localhost:8000/mcp \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}'
5. Connect MCP Inspector. Run the Inspector and point it at http://localhost:8000/mcp with transport Streamable HTTP and an Authorization: Bearer <your-token> header.
Tests — transport and auth behavior is covered by tests/test_config_transport.py and tests/test_auth.py; batch orchestration is covered by tests/test_batch_manager.py. Run with:
uv run pytest tests/
Building against a different EnergyPlus version
The Docker image bakes EnergyPlus 26.1.0 in by default. To pin a different release, override the build args below when building the image. You'll need:
EPLUS_VER: release version, e.g.25.1.0EPLUS_HASH: the short commit string NREL embeds in the release tarball filenameEPLUS_PREFIX: install path inside the container, e.g./app/software/EnergyPlusV25-1-0EPLUS_DIST_SUFFIX:Ubuntu22.04for EnergyPlus ≤ 25.1.0,Ubuntu24.04for ≥ 26.1.0
docker build \
--build-arg EPLUS_VER=25.1.0 \
--build-arg EPLUS_HASH=68a4a7c774 \
--build-arg EPLUS_PREFIX=/app/software/EnergyPlusV25-1-0 \
--build-arg EPLUS_DIST_SUFFIX=Ubuntu22.04 \
-t ubem-mcp-dev \
-f .devcontainer/Dockerfile .devcontainer
Heads up — three other places reference the install path (/app/software/EnergyPlusV26-1-0) and don't read the Dockerfile ARG, so if you override EPLUS_PREFIX you'll also need to update:
ubem-mcp-server/ubem_mcp_server/config.py—versionanddefault_installationubem-mcp-server/.vscode/mcp.json— the twoEnergyPlusV26-1-0strings- The client config JSON you created for Claude Desktop / VS Code / Cursor — only if it sets
EPLUS_IDD_PATHexplicitly
Available Tools
The server provides 41 tools organized into 6 categories:
🏙️ Batch Simulation (6 tools) — new in UBEM-MCP
discover_idf_files- Glob a directory for IDF files to build a batch's model listrun_batch_simulation- Run many EnergyPlus models concurrently as a background job; returns abatch_idimmediatelyget_batch_status- Poll overall/per-status progress of a batch jobget_batch_results- Get per-model results (success/failure, duration, output paths)list_batches- List all known batch jobs (in-memory + persisted on disk)cancel_batch- Cancel a batch job (best-effort for models already running)
🗂️ Model Config & Loading (9 tools)
load_idf_model- Load and validate IDF filesvalidate_idf- Comprehensive model validationlist_available_files- Browse sample files and weather datacopy_file- Intelligent file copying with path resolutionget_model_summary- Extract basic model informationcheck_simulation_settings- Review simulation control settingsmodify_simulation_control- Modify simulation parametersmodify_run_period- Adjust simulation time periodsget_server_configuration- Get server configuration info
🔍 Model Inspection (9 tools)
list_zones- List all thermal zones with propertiesget_surfaces- Get building surface informationget_materials- Extract material definitionsinspect_schedules- Analyze all schedule objectsinspect_people- Analyze occupancy settingsinspect_lights- Analyze lighting loadsinspect_electric_equipment- Analyze equipment loadsget_output_variables- Get/discover output variablesget_output_meters- Get/discover energy meters
⚙️ Model Modification (8 tools)
modify_people- Update occupancy settingsmodify_lights- Update lighting loadsmodify_electric_equipment- Update equipment loadschange_infiltration_by_mult- Modify infiltration ratesadd_window_film_outside- Add window filmsadd_coating_outside- Apply surface coatingsadd_output_variables- Add output variablesadd_output_meters- Add energy meters
🚀 Simulation & Results (4 tools)
run_energyplus_simulation- Execute a single simulationcreate_interactive_plot- Generate HTML visualizationsdiscover_hvac_loops- Find all HVAC loopsget_loop_topology- Get HVAC loop details
🖥️ Server Management (5 tools)
visualize_loop_diagram- Generate HVAC diagramsget_server_status- Check server healthget_server_logs- View recent logsget_error_logs- Get error logsclear_logs- Clear/rotate log files
Usage Examples
Basic Workflow
-
Load a model:
{ "tool": "load_idf_model", "arguments": { "idf_path": "sample_files/1ZoneUncontrolled.idf" } } -
Inspect zones:
{ "tool": "list_zones", "arguments": { "idf_path": "sample_files/1ZoneUncontrolled.idf" } } -
Run a single simulation:
{ "tool": "run_energyplus_simulation", "arguments": { "idf_path": "sample_files/1ZoneUncontrolled.idf", "weather_file": "sample_files/USA_CA_San.Francisco.Intl.AP.724940_TMY3.epw", "annual": true } }
Using with MCP Inspector
Test tools interactively (requires Node.js 18+):
# From the repo root, run the server inside the dev image under the Inspector
npx @modelcontextprotocol/inspector \
docker run --rm -i \
-v "$(pwd):/workspace" \
-w /workspace/ubem-mcp-server \
ubem-mcp-dev \
uv run python -m ubem_mcp_server.server
Batch Simulation
This is the key difference from single-model EnergyPlus MCP servers: run a whole portfolio of buildings and poll for results, instead of one blocking call per building.
Why polling, not a single blocking call: eppy's IDF class holds module-level global state, so concurrent simulations run as separate worker processes rather than in-process threads. And a portfolio of many buildings can take minutes to hours — far longer than an MCP client should have a single tool call block for. run_batch_simulation returns a batch_id immediately; you poll get_batch_status/get_batch_results until it's done.
-
Discover models in a directory:
{"tool": "discover_idf_files", "arguments": {"directory": "sample_files", "pattern": "*.idf"}} -
Submit the batch:
{ "tool": "run_batch_simulation", "arguments": { "models": ["sample_files/1ZoneUncontrolled.idf", "sample_files/1ZoneEvapCooler.idf"], "weather_file": "sample_files/USA_CA_San.Francisco.Intl.AP.724940_TMY3.epw", "continue_on_error": true } }Returns
{"batch_id": "...", "status": "queued", ...}. -
Poll for status:
{"tool": "get_batch_status", "arguments": {"batch_id": "<batch_id>"}}Repeat until
"is_complete": true. -
Get per-model results:
{"tool": "get_batch_results", "arguments": {"batch_id": "<batch_id>"}}
Per-model overrides are supported by passing dicts instead of plain path strings:
{
"tool": "run_batch_simulation",
"arguments": {
"models": [
{"idf_path": "bldg_001.idf", "label": "office_a"},
{"idf_path": "bldg_002.idf", "weather_file": "chicago.epw", "label": "office_b"}
],
"continue_on_error": false
}
}
With continue_on_error: false, the first model failure cancels remaining not-yet-started models in the batch (best-effort — an EnergyPlus subprocess already running in a worker can't be forcibly killed).
Architecture
The server follows a layered architecture:
┌─────────────────────────┐
│ MCP Protocol Layer │ FastMCP server handling client communications
├─────────────────────────┤
│ Tools Layer │ 41 tools organized into 6 categories
├─────────────────────────┤
│ Orchestration Layer │ EnergyPlus Manager, Batch Manager & Config Module
├─────────────────────────┤
│ EnergyPlus Integration │ Direct interface to simulation engine (per-model,
│ │ one worker process per concurrent batch model)
└─────────────────────────┘
Project Structure:
ubem-mcp-server/
├── ubem_mcp_server/
│ ├── server.py # FastMCP server with tools
│ ├── energyplus_tools.py # Core EnergyPlus integration (single model)
│ ├── batch_manager.py # Batch/portfolio orchestration (new in UBEM-MCP)
│ ├── config.py # Configuration management
│ └── utils/ # Specialized utilities
├── sample_files/ # Sample IDF and weather files
├── tests/ # Unit tests
└── pyproject.toml # Dependencies
Configuration
The server auto-detects EnergyPlus installation and uses sensible defaults. Configuration can be customized via environment variables:
EPLUS_IDD_PATH: Path to EnergyPlus IDD fileEPLUS_SAMPLE_PATH: Custom sample files directoryEPLUS_OUTPUT_PATH: Output directory for resultsUBEM_BATCH_MAX_WORKERS: Max concurrent simulation processes for batch jobs (default: CPU count)MCP_TRANSPORT,MCP_HTTP_HOST,MCP_HTTP_PORT,MCP_HTTP_PATH,MCP_TOKENS: streamable-HTTP transport settings (see Streamable HTTP Transport)
Troubleshooting
Common Issues:
- "IDD file not found": Ensure EnergyPlus is installed
- "Module not found": Run
uv syncto install dependencies - "Permission denied": Check file permissions
- "Simulation failed": Check EnergyPlus error messages in the model's output directory
- Batch stuck at
"is_complete": false: Checkget_batch_statusper-status counts — a very large batch with a lowmax_workersvalue simply takes longer; checkget_server_logsfor individual model errors
Debugging:
- Check server status:
get_server_status - View logs:
get_server_logs - Check errors:
get_error_logs - Check a batch job:
get_batch_status/get_batch_results
Contributing
- Fork the repository
- Create a feature branch
- Make changes with tests
- Run checks:
uv run ruff check uv run black . uv run pytest - Submit a pull request
Cite this work
UBEM-MCP is built on EnergyPlus-MCP. If you use UBEM-MCP in your research, please cite the original EnergyPlus-MCP paper — see NOTICE.md for the full citation and BibTeX entry.
License
This repository uses two BSD-3-Clause licenses side by side:
- LICENSE (TensorBuildLab) covers UBEM-MCP's own original code, including the batch simulation feature.
- License.txt / Copyright.txt (LBNL) covers the code carried over from EnergyPlus-MCP.
See NOTICE.md for exactly which files each license covers and the relationship between this project and EnergyPlus-MCP.
Government Rights Notice: The EnergyPlus-MCP portions of this software were developed under funding from the U.S. Department of Energy, and the U.S. Government consequently retains certain rights as described in License.txt.