MCP server for creating and manipulating Stella system dynamics models (.stmx files)
Project description
Stella MCP Server
A Model Context Protocol (MCP) server for creating and manipulating Stella system dynamics models. This enables AI assistants like Claude to programmatically build, read, validate, and save .stmx files in the XMILE format.
What is this for?
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:
- Create models from scratch - Build stock-and-flow diagrams programmatically
- Read existing models - Parse and understand .stmx files
- Validate models - Check for errors like undefined variables or missing connections
- Modify models - Add stocks, flows, auxiliaries, and connectors
- Save models - Export valid XMILE files that open in Stella Professional
This is particularly useful for:
- Teaching system dynamics modeling
- Rapid prototyping of models through natural language
- Batch creation or modification of models
- Documenting and explaining existing models
Installation
From PyPI
pip install stella-mcp
From source
git clone https://github.com/bradleylab/stella-mcp.git
cd stella-mcp
pip install -e .
Requirements
- Python 3.10+
mcp>=1.0.0
Configuration
Claude Desktop
Add to your claude_desktop_config.json:
{
"mcpServers": {
"stella": {
"command": "stella-mcp"
}
}
}
Claude Code
Add to your .claude/settings.json:
{
"mcpServers": {
"stella": {
"command": "stella-mcp"
}
}
}
Development mode
If running from source:
{
"mcpServers": {
"stella": {
"command": "python",
"args": ["-m", "stella_mcp.server"],
"cwd": "/path/to/stella-mcp"
}
}
}
Recommended Agent Workflow
For a new model:
create_modelwith a stablemodel_id.- Add stocks, flows, and auxiliaries.
- Run
sync_connectors_from_equations. - Run
inspect_modelwithinclude_validation=true. - Fix validation errors with
update_*,rename_variable, ordelete_variable. - Save with
save_model.
For imported models:
read_modelwithcompat_mode="permissive"to inspect warnings.- Run
inspect_modelto understand model structure. - Use
compat_mode="strict"before final save when round-trip fidelity matters.
Available Tools
Model Creation & I/O
| 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 |
Templates
| 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 session |
save_as_template |
Save the current model as a reusable user template (optional description/tags) |
Model Building
| Tool | Description |
|---|---|
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:
- Tools accept optional
model_idso one MCP session can manage multiple models safely. create_modelandread_modelset the session's currentmodel_idand return it.add_flowandadd_auxsupport optionalgraphical_functionpayloads (yptsplus exactly one ofxscaleorxpts).add_stock/add_flow/add_auxreject duplicate variable names across variable types;add_connectorrequires both variables to exist.set_connector_routingcan target a connector byconnector_uidor byfrom_var+to_var.save_modelandget_model_xmlacceptauto_layout(defaulttrue) andresolve_layout_violations(defaultfalse).read_model,save_model, andget_model_xmlacceptcompat_mode:permissive(default): continue with warningsstrict: fail on compatibility issues
set_module_styleupdates module view styling and persists those attributes in XMILE view<group .../>elements.save_as_templatewrites user templates to~/.stella-mcp/templatesby default (override viaSTELLA_MCP_TEMPLATE_DIR) and stores metadata in a.meta.jsonsidecar.- Tool failures return structured MCP errors with
error.code,error.category, anderror.message.
Model Inspection
| Tool | Description |
|---|---|
list_models |
List available session 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 |
Tool Payload Examples
Create and switch between session models:
{"name":"create_model","arguments":{"name":"Population","model_id":"pop_v1"}}
{"name":"create_model","arguments":{"name":"Carbon","model_id":"carbon_v1"}}
{"name":"list_models","arguments":{}}
{"name":"inspect_model","arguments":{"model_id":"sir_baseline","include_validation":true}}
List and load templates:
{"name":"list_templates","arguments":{}}
{"name":"list_templates","arguments":{"source":"builtin","query":"epidem","tags":["epidemiology"]}}
{"name":"get_template_info","arguments":{"template_name":"sir"}}
{"name":"load_template","arguments":{"template_name":"sir","model_id":"sir_baseline"}}
Save current model as a user template:
{"name":"save_as_template","arguments":{"model_id":"pop_v1","template_name":"my_population_template","description":"Baseline single-stock growth starter","tags":["intro","population"]}}
Create and manage modules:
{"name":"create_module","arguments":{"model_id":"sir_baseline","name":"Disease Dynamics","members":["Susceptible","Infected","Recovered"]}}
{"name":"add_to_module","arguments":{"model_id":"sir_baseline","module_name":"Disease Dynamics","members":["infection","recovery"]}}
{"name":"list_modules","arguments":{"model_id":"sir_baseline"}}
{"name":"remove_from_module","arguments":{"model_id":"sir_baseline","module_name":"Disease Dynamics","members":["recovery"]}}
{"name":"rename_module","arguments":{"model_id":"sir_baseline","module_name":"Disease Dynamics","new_name":"Disease Core"}}
{"name":"delete_module","arguments":{"model_id":"sir_baseline","module_name":"Disease Core"}}
Rename and delete variables safely:
{"name":"rename_variable","arguments":{"model_id":"sir_baseline","old_name":"population_total","new_name":"total_population"}}
{"name":"delete_variable","arguments":{"model_id":"sir_baseline","name":"recovery"}}
{"name":"delete_variable","arguments":{"model_id":"sir_baseline","name":"Susceptible","force":true}}
Update an existing variable:
{"name":"update_flow","arguments":{"model_id":"pop_v1","name":"growth","equation":"Population * growth_rate * stress_modifier"}}
Infer missing connectors from equations:
{"name":"sync_connectors_from_equations","arguments":{"model_id":"pop_v1"}}
Set module view geometry directly:
{"name":"set_module_view","arguments":{"model_id":"sir_baseline","module_name":"Disease Dynamics","x":420,"y":280,"width":420,"height":240}}
Set module view style:
{"name":"set_module_style","arguments":{"model_id":"sir_baseline","module_name":"Disease Dynamics","border_color":"#666666","background":"#FFF7E6","font_color":"#333333","font_size":"10pt","label_side":"top"}}
Auto-place module boxes from current member positions:
{"name":"auto_place_module_boxes","arguments":{"model_id":"sir_baseline","padding":40,"only_missing":true}}
Target a specific model in later calls:
{"name":"add_stock","arguments":{"model_id":"pop_v1","name":"Population","initial_value":"100"}}
Read with strict compatibility checks:
{"name":"read_model","arguments":{"filepath":"./external_model.stmx","model_id":"imported","compat_mode":"strict"}}
Preview XML in permissive mode (default) and return compatibility warnings when present:
{"name":"get_model_xml","arguments":{"model_id":"imported","compat_mode":"permissive"}}
Valid graphical function payload:
{
"name": "add_aux",
"arguments": {
"model_id": "pop_v1",
"name": "lookup_rate",
"equation": "GRAPH(Time)",
"graphical_function": {
"xscale": {"min": 0, "max": 100},
"ypts": [0.1, 0.2, 0.4, 0.6],
"type": "continuous"
}
}
}
Invalid graphical function payload (rejected):
{
"name": "add_aux",
"arguments": {
"name": "bad_lookup",
"equation": "GRAPH(Time)",
"graphical_function": {
"xscale": {"min": 0, "max": 100},
"xpts": [0, 10, 20, 30],
"ypts": [0.1, 0.2, 0.4, 0.6]
}
}
}
Example Usage
Creating a simple population model
User: Create a simple exponential growth model with a population starting at 100
and a growth rate of 0.1 per year
Claude: [Uses create_model, add_stock, add_aux, add_flow, add_connector, save_model]
Creates population_growth.stmx with:
- Stock: Population (initial=100)
- Aux: growth_rate (0.1)
- Flow: growth (Population * growth_rate) into Population
Reading and analyzing an existing model
User: Read the carbon cycle model and explain what it does
Claude: [Uses read_model, list_variables]
This model has 3 stocks (Atmosphere, Land Biota, Soil) and 6 flows
representing carbon exchange through photosynthesis, respiration...
Building a biogeochemical model
User: Create a two-box ocean model with surface and deep nutrients
Claude: [Uses create_model, add_stock (x4), add_aux (x8), add_flow (x6), save_model]
Creates a model with nutrient cycling between surface and deep ocean
including upwelling, downwelling, biological uptake, and remineralization
Validation
The validate_model tool checks for:
- Undefined variables - References to variables that don't exist
- Mass balance issues - Stocks without flows, flows referencing non-existent stocks
- Missing connections - Equations using variables without connectors (warning)
- Connector endpoint integrity - Connectors pointing at missing variables (error)
- Orphan flows - Flows not connected to any stock
- Circular dependencies - Infinite loops in auxiliary calculations
- Module integrity - Empty modules (warning) and modules referencing missing members (error)
XMILE Compatibility
- Output files use the XMILE standard
- Compatible with Stella Professional 1.9+ and Stella Architect
- Auto-layout positions elements reasonably; adjust manually in Stella if needed
- Variable names with spaces are converted to underscores internally
- Parser normalizes imported stock inflow/outflow and connector endpoint references
- Time-step export avoids lossy reciprocal rounding (non-exact reciprocals are exported as plain
dt) - Import/export preserves unknown attrs/elements on supported sections (header, sim_specs, variables, views/model extras) to reduce round-trip data loss
- Compatibility corpus regression tests live in
tests/fixtures/compat_corpus/and run in CI - Maintainer helper:
python scripts/sync_compat_corpus_manifest.py --checkvalidates corpus manifest sync
Project Structure
stella-mcp/
├── README.md
├── LICENSE
├── pyproject.toml
└── stella_mcp/
├── __init__.py
├── server.py # MCP server wiring + schemas
├── tool_handlers.py # Tool handler implementations/registration
├── tool_schemas.py # MCP tool schema definitions
├── xmile.py # Core model types + layout logic
├── xmile_io.py # XMILE parsing/export helpers
└── validator.py # Model validation logic
Contributing
Contributions are welcome! Please feel free to submit issues or pull requests.
Maintainer Release
PyPI publishing is handled by .github/workflows/publish.yml using PyPI Trusted
Publishing. To release a new version:
- Update the version in
pyproject.tomlandstella_mcp/__init__.py. - Merge the release changes to
main. - Create and publish a GitHub release with a matching tag, for example
v0.5.0.
The GitHub release event builds the source distribution and wheel, then publishes them to PyPI through the configured trusted publisher.
License
MIT License - see LICENSE for details.
Acknowledgments
- Model Context Protocol by Anthropic
- ISEE Systems for Stella and the XMILE format
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file stella_mcp-0.6.0.tar.gz.
File metadata
- Download URL: stella_mcp-0.6.0.tar.gz
- Upload date:
- Size: 84.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2fcb171aa4926304acabc6aaadd3d621341356d12c85d54e3b3056b76184312b
|
|
| MD5 |
c8a9062a498ef634a79751e87c6669c4
|
|
| BLAKE2b-256 |
8f36ef8ec408f05a65c3b73c20a23c8f0e20a13fc9cb6683476cc5ae552968a9
|
Provenance
The following attestation bundles were made for stella_mcp-0.6.0.tar.gz:
Publisher:
publish.yml on bradleylab/stella-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stella_mcp-0.6.0.tar.gz -
Subject digest:
2fcb171aa4926304acabc6aaadd3d621341356d12c85d54e3b3056b76184312b - Sigstore transparency entry: 1607533851
- Sigstore integration time:
-
Permalink:
bradleylab/stella-mcp@24558c303dce54f0961fa6a3ca87593676899a95 -
Branch / Tag:
refs/tags/v0.6.0 - Owner: https://github.com/bradleylab
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@24558c303dce54f0961fa6a3ca87593676899a95 -
Trigger Event:
release
-
Statement type:
File details
Details for the file stella_mcp-0.6.0-py3-none-any.whl.
File metadata
- Download URL: stella_mcp-0.6.0-py3-none-any.whl
- Upload date:
- Size: 65.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
04af7007d1016ea7b745c2fa047401157aee356cf85c130c5f5523f089fa2fd0
|
|
| MD5 |
835885b07ca30ecedd5190815b98f654
|
|
| BLAKE2b-256 |
91cc52e6228408b7425a100b30ca3d350b8168f80290aaba90fae753b3f2f69a
|
Provenance
The following attestation bundles were made for stella_mcp-0.6.0-py3-none-any.whl:
Publisher:
publish.yml on bradleylab/stella-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stella_mcp-0.6.0-py3-none-any.whl -
Subject digest:
04af7007d1016ea7b745c2fa047401157aee356cf85c130c5f5523f089fa2fd0 - Sigstore transparency entry: 1607533914
- Sigstore integration time:
-
Permalink:
bradleylab/stella-mcp@24558c303dce54f0961fa6a3ca87593676899a95 -
Branch / Tag:
refs/tags/v0.6.0 - Owner: https://github.com/bradleylab
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@24558c303dce54f0961fa6a3ca87593676899a95 -
Trigger Event:
release
-
Statement type: