The full upstream README, mirrored here for reference. Install config, tool schemas, adoption signals, and an original overview live on the Stella MCP listing page.
A vendor-neutral Model Context Protocol (MCP)
server for creating and manipulating Stella
system dynamics models. Any compliant MCP client can build, read, validate, and
save .stmx files in the XMILE format; optional host features still vary.
Stella is a system dynamics modeling tool used for simulating complex systems in fields like ecology, biogeochemistry, economics, and engineering. This MCP server allows AI assistants to:
This is particularly useful for:
mcp>=2.0.0,<3If you have uv installed, the lowest-friction configuration runs the published package directly:
Add to your claude_desktop_config.json:
Add to your .claude/settings.json:
If running from source:
For a new model:
create_workspace and carry the returned
workspace_id through stateful calls. Legacy stdio clients may omit it.build_model with a stable model_id and the full set of stocks,
auxiliaries, and flows in one call (connector sync and validation run by
default, so the response doubles as an inspection).update_*, rename_variable, or delete_variable.add_variables (batch) or the single-add tools.simulate to sanity-check behavior (requires the sim extra).save_model.For imported models:
read_model with compat_mode="permissive" to inspect warnings.inspect_model to understand model structure.compat_mode="strict" before final save when round-trip fidelity matters.| Tool | Description |
|---|---|
create_model | Create a new model with name and time settings (start, stop, dt, method) |
set_sim_specs | Update simulation time settings on an existing model |
read_model | Load an existing .stmx file |
save_model | Save model to a .stmx file |
delete_model | Remove a model from the workspace (saved files untouched) |
| Tool | Description |
|---|---|
list_templates | List built-in and user-defined templates (supports source/query/tag filters) |
get_template_info | Get detailed metadata for one template |
load_template | Load a template as a model in the current workspace |
save_as_template | Save the current model as a reusable user template (optional description/tags) |
| Tool | Description |
|---|---|
build_model | Create and populate a model in one call (atomic batch) |
add_variables | Add multiple variables/connectors/modules to an existing model (atomic batch) |
add_stock | Add a stock (reservoir) with initial value and units |
add_flow | Add a flow between stocks with an equation |
add_aux | Add an auxiliary variable (parameter or calculation) |
update_stock | Update stock fields while preserving relationships |
update_flow | Update flow fields while preserving stock links |
update_aux | Update auxiliary variable fields |
add_connector | Add a dependency connector between variables |
sync_connectors_from_equations | Add missing dependency connectors inferred from equations |
set_connector_routing | Set connector angle and explicit waypoint routing metadata |
rename_variable | Rename a stock/flow/aux and update references in equations/connectors/modules |
delete_variable | Delete a stock/flow/aux with consistency checks and cleanup |
create_module | Create a logical module/group of variables |
add_to_module | Add variables to an existing module/group |
remove_from_module | Remove variables from a module/group |
rename_module | Rename a module/group |
delete_module | Delete a module/group |
set_module_view | Set explicit module box position/size on the diagram |
set_module_style | Set module box style (border/background/font/label side) on the diagram |
auto_place_module_boxes | Auto-place module boxes around their members |
Notes:
create_workspace once and include its returned
workspace_id in stateful calls. Modern tool discovery marks that field as
required on stateful tools. The ID routes application state; it is not an
authorization credential.workspace_id and use one
process-local compatibility workspace; legacy discovery keeps the field
optional.model_id so one workspace can manage multiple models safely.create_model and read_model set the workspace's current model_id and return it.add_flow and add_aux support optional graphical_function payloads (ypts plus exactly one of xscale or xpts).add_stock/add_flow/add_aux reject duplicate variable names across variable types; add_connector requires both variables to exist.set_connector_routing can target a connector by connector_uid or by from_var + to_var.save_model and get_model_xml accept auto_layout (default true) and resolve_layout_violations (default false).save_model, get_model_xml, and render_diagram return the latest layout
viewport, metrics, and warnings in structured content. Their text result names
any non-clean layout warning codes.read_model, save_model, and get_model_xml accept compat_mode:
permissive (default): continue with warningsstrict: fail on compatibility issuesset_module_style updates module view styling and persists those attributes in XMILE view <group .../> elements.save_as_template writes user templates to ~/.stella-mcp/templates by default (override via STELLA_MCP_TEMPLATE_DIR) and stores metadata in a .meta.json sidecar.error.code, error.category, and error.message.structuredContent described by its JSON Schema 2020-12 outputSchema.| Tool | Description |
|---|---|
create_workspace | Create an isolated workspace, optionally with a caller-selected lifetime |
revoke_workspace | Revoke a workspace and discard its in-memory models |
| Tool | Description |
|---|---|
list_models | List available workspace model IDs and indicate the current model |
inspect_model | Return a structured model summary for agent inspection |
list_modules | List modules/groups in the current model |
list_connectors | List connector IDs, endpoints, angles, and routing metadata |
list_variables | List all stocks, flows, and auxiliaries |
validate_model | Check for errors (undefined variables, missing connections, etc.) |
get_model_xml | Preview the XMILE XML output |
render_diagram | Render the model as an SVG stock-and-flow diagram |
simulate | Run the model via PySD and return time series + summaries (sim extra) |
compare_scenarios | Run named what-if override sets against a baseline and report deltas (sim extra) |
sensitivity_analysis | Sweep parameters one-at-a-time and rank their effect on an output metric (sim extra) |
calibrate | Fit constant parameters to an observed time-series (inverse of simulate) (sim extra) |
build_model creates and populates a model in one call. Items apply in the
order stocks → auxs → flows → connectors → modules; the whole batch is
all-or-nothing, and on failure the error names the failing item
(error.stage + error.index). The same item arrays work on an existing
model via add_variables.
Connector sync and validation run by default (disable with
"sync_connectors": false / "validate": false); the response includes the
full structured model summary, so no follow-up inspect_model call is needed.
Create and switch between workspace models:
List and load templates:
Save current model as a user template:
Create and manage modules:
Rename and delete variables safely:
Update an existing variable:
Infer missing connectors from equations:
Set module view geometry directly:
Set module view style:
Auto-place module boxes from current member positions:
Target a specific model in later calls:
Read with strict compatibility checks:
Preview XML in permissive mode (default) and return compatibility warnings when present:
Valid graphical function payload:
Invalid graphical function payload (rejected):
The render_diagram tool renders the model as an SVG stock-and-flow diagram
— stocks as rectangles, auxiliaries as circles, flows as valved pipes
(clouds mark sources/sinks), and dependency connectors as routed polylines. The SVG is
returned inline so an agent can inspect the layout, and optionally written
to a file you can open in any browser. It runs auto-layout first by default,
so a freshly built model renders without manual positioning.
The diagram below is the built-in sir template rendered by render_diagram
(no manual positioning):
The simulate tool runs the current model and returns downsampled time
series plus per-variable summaries (initial/final/min/max), closing the
build→verify loop without opening Stella. It requires the optional
PySD dependency:
Notes and caveats:
method is RK4 simulate
with Euler and the response carries a warning. Every PySD-backed response
identifies the installed PySD version, actual method, declared method,
unsupported-feature preflight, and warnings.unsupported_model_feature error rather than being silently scalarized or
flattened.overrides accepts variable names in display ("growth rate") or
underscore (growth_rate) form and replaces the variable with a constant.save_results_csv writes the full-resolution results table with a time
column.The compare_scenarios tool answers "what happens under these alternative
assumptions?" — it runs several named override sets against a baseline (the
unmodified model by default) and reports how each diverges. Also requires the
sim extra.
Each scenario reports its own downsampled series plus delta_vs_baseline per
variable: final_abs, final_pct (percent change of the final value), and
max_abs. Notes:
warnings without aborting the others; final_pct is null when the
baseline final is zero (no divide-by-zero).baseline is optional — pass an override set to measure deltas against, or
omit it to compare against the unmodified model.save_comparison_csv writes a wide table with one column per
variable__scenario (and variable__baseline).The sensitivity_analysis tool answers "which parameters actually move the
outcome?" — it sweeps each parameter one at a time across a range (holding the
others at their baseline) and reports how a single chosen output metric
responds. Also requires the sim extra.
For each parameter it returns the metric at every swept value, a
range_sensitivity (the metric's average slope across the swept range), and a
baseline-normalized elasticity (≈ Δoutput% / Δparam%) so parameters can be
ranked by influence. Notes:
mode accepts "oat"; full-factorial (grid) and
Monte-Carlo sampling are reserved for a future release.metric is one of final, max, min, mean, or time_to_threshold
(which needs an output.threshold and reports the first time the series
crosses it). max/min/mean cover finite values only; a non-finite or
never-crossing run reports null for that point with a warning.start/stop/steps (evenly spaced, steps
≥ 2) or an explicit values list (≥ 2 entries).max_runs (default 200) caps the total swept runs; an oversized sweep
errors rather than silently truncating. OAT runs are a sum across
parameters, not a product, so the cap only trips on genuinely large sweeps.elasticity is null when it cannot be defined (a non-constant parameter,
or a zero baseline metric/parameter); range_sensitivity is still reported.save_sweep_csv writes a long parameter, value, metric table.The calibrate tool is the inverse of simulate: given an observed
time-series, it fits constant parameters so the model reproduces the data. Also
requires the sim extra.
It returns the fitted parameters (each with its bounds, an at_bound flag, and
a linearized std_error), the weighted objective trajectory
(initial/final weighted_sse and weighted_rmse), native-unit error
metrics for each target, optimizer status/configuration, and warnings. Notes:
least_squares (default) is local and fast and reports a
std_error; differential_evolution is global, stochastic, seeded for
reproducibility, and requires min/max bounds on every parameter. Its
maxiter generation cap defaults to 100.initial defaults to the model's current constant value.
Bounds are optional for least_squares, required for differential_evolution.std_error is a linearized approximation, not a posterior: it is the
covariance σ²·(JᵀJ)⁻¹ and is reported only when there are more observations
than parameters, the Jacobian is well-conditioned, and no parameter sits on a
bound; otherwise it is null with a warning. differential_evolution
returns null (no Jacobian). Under non-default weights, the standard-error
interpretation holds only for inverse-σ weights.[start, stop] window and the
simulation is linearly interpolated onto the observation times. Observation
times outside the window are rejected (no extrapolation). All targets
share one strictly-increasing time grid; observations are loaded inline or
from a csv_path (first column time, the rest targets).weights are residual multipliers: the optimizer uses
weight * (simulated - observed). Inverse measurement-standard-deviation
values give normalized residuals. Because weighted errors may mix units,
target_metrics reports an unweighted SSE and RMSE in each target's native
units; no aggregate native-unit RMSE is reported. save_fit_csv writes a long
time, target, observed, fitted table; return_fit_series attaches the
best-fit series. The model is compiled once and reused across the whole fit.Beyond tools, the server exposes MCP-native affordances:
readOnlyHint,
destructiveHint, idempotentHint) so clients can manage permissions and
parallelize read-only calls. Inspection tools (inspect_model,
validate_model, list_*, get_model_xml) are read-only; delete_* are
marked destructive.stella://templates/{name} — a built-in or user template's .stmxstella://workspaces/{workspace_id}/models/{model_id} — an explicit
workspace model's current XMILE exportstella://models/{model_id} — the legacy stdio compatibility workspace onlybuild-stella-model prompt (argument: description) encodes
the recommended build → validate → simulate → render → save workflow, so it
is discoverable inside MCP clients.The validate_model tool checks for:
stock-units/time-unit
when every attached stock shares the same units (warning; conservative —
stays silent on conversion flows and anything it can't confidently parse)permissive import/export preserves supported content and the selected
unsupported XML fragments where practical, while returning explicit warnings.
Editing supported variables does not guarantee references inside preserved-only
fragments are updated.strict import/export rejects arrays, compositional module instances,
additional top-level models, and confirmed Stella/XMILE reserved identifiers.
Arrays and nested models are not implemented features in 0.14.beta and gamma are preserved with warnings in
permissive mode and rejected in strict mode. The built-in SIR template uses
transmission_rate and recovery_rate so Stella does not rename them on save.layout.* warning; zero crossings are not promised for arbitrary non-planar
graphs.dt)tests/fixtures/compat_corpus/.
A pinned, attributed subset of SDXorg test-models lives in
tests/fixtures/external_corpus/; both run offline in CI.python scripts/sync_compat_corpus_manifest.py --check validates corpus manifest syncThe repository test suite covers the MCP stdio protocol, workspace isolation, model construction, validation, SVG rendering, XMILE import/export, simulation, scenario analysis, sensitivity analysis, calibration, and package installation. Run it from a source checkout with:
Pinned Stella-saved and external XMILE fixtures live under tests/fixtures/ and
run offline in CI. Generated reports, local planning files, and manual review
artifacts are intentionally not committed to the source tree.
See docs/architecture.md for dependency boundaries,
module ownership, workspace lifecycle, and compatibility contracts.
Contributions are welcome! Please feel free to submit issues or pull requests.
PyPI publishing is handled by .github/workflows/publish.yml using PyPI Trusted
Publishing. To release a new version:
pyproject.toml, stella_mcp/__init__.py,
CITATION.cff, and CHANGELOG.md; keep the citation and changelog release
dates identical.uv lock --check, the core and simulation test suites, the MCP-floor
suite, and the package job. Prepare the draft GitHub release notes from the
matching CHANGELOG.md entry.main and v* tag
rules, the pypi environment's protected-tag policy, and the configured
PyPI Trusted Publisher.main CI run at the exact merge commit. If the Chicago
release date has changed, correct the metadata through another reviewed pull
request before continuing.pypi job's configured deployment policy accepts only tags matching the
protected v* release-tag policy before Trusted Publishing can upload the
verified artifacts.Tagging, draft creation, and publication are distinct approval gates. Do not move or replace a public tag or uploaded distribution to repair a release.
MIT License - see LICENSE for details.