Metadata-Version: 2.4
Name: pypsamcp
Version: 0.2.0
Summary: PyPSA MCP: PyPSA Energy Modeling for LLMs
Author-email: Carlos Gaete <cdgaete@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/cdgaete/pypsa-mcp
Project-URL: Bug Tracker, https://github.com/cdgaete/pypsa-mcp/issues
Project-URL: Source Code, https://github.com/cdgaete/pypsa-mcp
Keywords: mcp,pypsa,energy,modeling,claude,ai,assistant
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Scientific/Engineering :: Physics
Classifier: Topic :: Utilities
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: fastmcp>=2.2.0
Requires-Dist: pypsa>=1.1.0
Requires-Dist: highspy>=1.10.0
Provides-Extra: dev
Requires-Dist: pytest>=8.3.5; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
Requires-Dist: black>=24.3.0; extra == "dev"
Requires-Dist: ruff>=0.3.4; extra == "dev"
Requires-Dist: build>=1.2.1; extra == "dev"
Dynamic: license-file

# PyPSA MCP

A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that lets LLMs like Claude build, configure, and optimize energy system models using [PyPSA](https://github.com/PyPSA/PyPSA) through natural language.

## Demo

https://github.com/user-attachments/assets/5633a431-7c3b-4a2f-9a9e-395dcbbb2e29

---

## What's New in 0.2.0

### Bug fixes
- **Rolling horizon** — fixed `setting an array element with a sequence` crash caused by PyPSA's `_set_dynamic_data` mishandling single-column pnl DataFrames when slicing snapshot windows. Fixed via a `dup2`-level `_patch_set_dynamic_data` context manager.
- **MGA single-column crash** — same root cause in `optimize_mga` post-processing. Fixed by patching both `pypsa.optimization.common` and `pypsa.optimization.optimize` before each MGA call.
- **Stale results after infeasible solve** — `run_simulation` was returning cached dispatch from a prior successful solve when a subsequent solve was infeasible. Now returns a clean `null` response with an explicit `infeasibility_note`.
- **`compute_infeasibilities` with HiGHS** — instead of silently returning wrong results, the server now correctly reports that this flag requires Gurobi in PyPSA 1.x and suggests the load-shedding VoLL workaround.
- **HiGHS stdout pollution** — HiGHS writes solver logs via C-level stdio, bypassing Python's `sys.stdout`. Fixed by redirecting `fd 1 → fd 2` with `os.dup2` around every solver call, keeping the MCP stdio stream clean.
- **Silent fallbacks in statistics** — replaced silent zero-fill fallbacks with fail-fast errors that include actionable messages.

### Simulation modes validated end-to-end
All 8 modes now confirmed working: `pf`, `lpf`, `optimize`, `mga`, `security_constrained`, `rolling_horizon`, `transmission_expansion_iterative`, `optimize_and_pf`.

---

## Features

**22 MCP tools** across 10 modules:

| Module | Tools |
|---|---|
| Management | `create_energy_model`, `list_models`, `delete_model`, `export_model_summary` |
| Discovery | `list_component_types`, `describe_component` |
| Components | `add_component`, `update_component`, `remove_component`, `query_components` |
| Time config | `configure_time` (snapshots, investment periods, scenarios, CVaR risk preference) |
| Simulation | `run_simulation` (8 modes) |
| Statistics | `get_statistics` (19 metrics) |
| Clustering | `cluster_network` (spatial: kmeans/hac/greedy; temporal: resample/downsample/segment) |
| I/O | `network_io` (export/import NetCDF & CSV, copy, merge, consistency check) |
| Deprecated | `add_bus`, `add_generator`, `add_load`, `add_line`, `set_snapshots`, `run_powerflow`, `run_optimization` |

**13 component types:** Bus, Generator, Load, Line, Link, StorageUnit, Store, Transformer, ShuntImpedance, Carrier, GlobalConstraint, LineType, TransformerType.

**Simulation modes:**
- `optimize` — linear OPF, capacity expansion, CO2 constraints, multi-investment periods, unit commitment
- `rolling_horizon` — limited-foresight dispatch with configurable horizon and overlap
- `mga` — Modeling to Generate Alternatives (near-optimal solution space exploration)
- `security_constrained` — N-1 contingency-constrained OPF
- `transmission_expansion_iterative` — iterative TEP with PTDF-based congestion
- `pf` / `lpf` — non-linear and linearized power flow
- `optimize_and_pf` — optimization followed by non-linear power flow

**Advanced modelling:** sector coupling (Links with multiple outputs), CVaR risk preference, scenario-based optimization, temporal/spatial network clustering, load shedding via VoLL dummy generators.

---

## Installation

### Requirements

- Python 3.10+
- [uv](https://github.com/astral-sh/uv) (recommended)

### From PyPI

```bash
pip install pypsamcp
# or
uv pip install pypsamcp
```

### Running the server

```bash
pypsamcp
```

### Claude Desktop configuration

Locate `~/.config/Claude/config.json` and add:

```json
"mcpServers": {
  "PyPSA MCP": {
    "command": "uv",
    "args": ["run", "--with", "pypsamcp", "pypsamcp"]
  }
}
```

Restart Claude Desktop after saving.

### Development installation

```bash
git clone https://github.com/cdgaete/pypsa-mcp.git
cd pypsa-mcp
uv pip install -e ".[dev]"
python -m pypsamcp.server
```

For development with Claude Desktop, point directly to the virtualenv Python so source changes are picked up immediately:

```json
"mcpServers": {
  "PyPSA-MCP-Dev": {
    "command": "/home/carlos/projects/pypsa-mcp/.venv/bin/python",
    "args": ["-m", "pypsamcp.server"]
  }
}
```

---

## Usage examples

### Minimal dispatch model

```text
Build a single-bus model with coal (300 MW, €20/MWh), gas (200 MW, €80/MWh),
and solar (150 MW, free) over 24 hourly snapshots. Add a diurnal load profile
peaking at 310 MW. Run OPF and explain the dispatch and marginal prices.
```

### Sector coupling — power + hydrogen + heat

```text
Build a model with an AC bus, H2 bus, and heat bus. Add 400 MW wind,
a 200 MW gas backup, an extendable PEM electrolyzer (70% efficiency,
€120k/MW), a heat pump (COP=3, €60k/MW), a district heating load,
and a flat 20 MW H2 industrial demand. Run OPF and compare the heat pump
against a gas boiler alternative.
```

### Transmission expansion planning

```text
Build a 3-bus ring (North: coal + wind, South: gas + heavy load, East: solar + load).
Set all lines to 50 MW with capital cost. Run OPF to show congestion and LMP divergence,
then run transmission_expansion_iterative to find the optimal line investments.
```

### Rolling horizon with battery

```text
Add a 100 MW / 4h battery to the dispatch model and run rolling_horizon
with horizon=6 and overlap=2. Compare the battery's intraday arbitrage
against perfect-foresight OPF.
```

### Multi-investment periods

```text
Build a model with two planning years (2025, 2030). Solar capital cost drops
from €50k/MW in 2025 to €30k/MW in 2030. Coal is fixed. Show when the
optimizer invests in solar and the resulting scarcity prices.
```

---

## Known limitations

- `compute_infeasibilities=True` requires Gurobi. With HiGHS, diagnose infeasibility using load-shedding generators (set `marginal_cost` to a high VoLL such as €10,000/MWh on each bus).
- Temporal clustering with `method="segment"` requires `tsam` (`pip install tsam`).
- CVaR scenario-differentiated time series (different p_max_pu or marginal costs per scenario) must be set manually via `update_component` with per-snapshot values after scenario configuration.
- Model state is in-memory only; models are lost on server restart. Use `network_io` with `operation="export_netcdf"` to persist, and `operation="import_netcdf"` to reload.

---

## Changelog

### 0.2.0 (2026-03-20)

**Bug fixes**
- Fixed rolling horizon crash (`setting an array element with a sequence`) via `_patch_set_dynamic_data` context manager
- Fixed MGA post-processing crash for single-component models via `_patch_single_column_pnl`
- Fixed stale results returned after infeasible solve — now returns clean null response
- Fixed `compute_infeasibilities` silently returning wrong results with HiGHS — now returns actionable guidance
- Fixed HiGHS C-level stdout pollution via `os.dup2` fd redirection in `stdout_to_stderr` context manager
- Fixed silent fallbacks in `get_statistics` — replaced with fail-fast errors

**Confirmed working: all 8 simulation modes, all 19 statistics metrics, all 13 component types**

### 0.1.4

- Initial public release with 22 tools across 10 modules
- Simulation: pf, lpf, optimize, mga, security_constrained, rolling_horizon, transmission_expansion_iterative, optimize_and_pf
- Components: full CRUD for all 13 PyPSA component types
- Statistics: 19 metrics including capacity factors, energy balance, market values, transmission
- Clustering: spatial (kmeans, hac, greedy_modularity) and temporal (resample, downsample, segment)
- I/O: NetCDF/CSV export-import, copy, merge, consistency check

---

## License

MIT — see [LICENSE](LICENSE).

## Acknowledgments

- [PyPSA](https://github.com/PyPSA/PyPSA) — power system modelling framework
- [FastMCP](https://github.com/jlowin/fastmcp) — MCP server framework
- [HiGHS](https://highs.dev/) — open-source LP/MIP solver
