Stella MCP Server
The Stella MCP Server enables AI assistants to programmatically create, read, modify, validate, and save Stella system dynamics models (.stmx files in XMILE format) via the Model Context Protocol.
Model I/O: Create new models with custom time settings, load existing
.stmxfiles, and save models to disk.Model Building: Add stocks, flows, auxiliary variables, and connectors; supports graphical functions (lookup tables) and automatic positioning.
Variable Management: Rename and delete variables with consistency checks (updates references in equations, connectors, and modules).
Module Management: Create, rename, delete, and style modules/groups; assign member variables and auto-place boxes.
Model Inspection: List variables, modules, and connectors; preview raw XMILE XML output.
Validation: Check for undefined variables, mass balance issues, missing connections, orphan flows, circular dependencies, and module integrity.
Templates: List, inspect, load, and save reusable built-in or user-defined templates.
Multi-Model Sessions: Manage multiple models concurrently via an optional
model_idparameter.Compatibility Modes: Read, save, and export with permissive or strict XMILE compatibility checks.
Connector Routing: Set connector angles and waypoints.
Error Handling: Structured errors with codes, categories, and messages.
Enables the creation, reading, and validation of Stella system dynamics models by programmatically generating and parsing XMILE-compliant XML files.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Stella MCP Servercreate a population model with a 5% growth rate starting at 100 people"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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
Related MCP server: OpenKer Modeler MCP Server
Installation
From PyPI
pip install stella-mcpFrom 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
Via uvx (no install required)
If you have uv installed, the lowest-friction configuration runs the published package directly:
{
"mcpServers": {
"stella": {
"command": "uvx",
"args": ["stella-mcp"]
}
}
}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:
build_modelwith a stablemodel_idand 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).Fix validation errors with
update_*,rename_variable, ordelete_variable.Extend incrementally with
add_variables(batch) or the single-add tools.simulateto sanity-check behavior (requires thesimextra).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 a new model with name and time settings (start, stop, dt, method) |
| Update simulation time settings on an existing model |
| Load an existing .stmx file |
| Save model to a .stmx file |
| Remove a model from the session (saved files untouched) |
Templates
Tool | Description |
| List built-in and user-defined templates (supports source/query/tag filters) |
| Get detailed metadata for one template |
| Load a template as a model in the current session |
| Save the current model as a reusable user template (optional description/tags) |
Model Building
Tool | Description |
| Create and populate a model in one call (atomic batch) |
| Add multiple variables/connectors/modules to an existing model (atomic batch) |
| Add a stock (reservoir) with initial value and units |
| Add a flow between stocks with an equation |
| Add an auxiliary variable (parameter or calculation) |
| Update stock fields while preserving relationships |
| Update flow fields while preserving stock links |
| Update auxiliary variable fields |
| Add a dependency connector between variables |
| Add missing dependency connectors inferred from equations |
| Set connector angle and explicit waypoint routing metadata |
| Rename a stock/flow/aux and update references in equations/connectors/modules |
| Delete a stock/flow/aux with consistency checks and cleanup |
| Create a logical module/group of variables |
| Add variables to an existing module/group |
| Remove variables from a module/group |
| Rename a module/group |
| Delete a module/group |
| Set explicit module box position/size on the diagram |
| Set module box style (border/background/font/label side) on the diagram |
| 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 available session model IDs and indicate the current model |
| Return a structured model summary for agent inspection |
| List modules/groups in the current model |
| List connector IDs, endpoints, angles, and routing metadata |
| List all stocks, flows, and auxiliaries |
| Check for errors (undefined variables, missing connections, etc.) |
| Preview the XMILE XML output |
| Render the model as an SVG stock-and-flow diagram |
| Run the model via PySD and return time series + summaries ( |
| Run named what-if override sets against a baseline and report deltas ( |
| Sweep parameters one-at-a-time and rank their effect on an output metric ( |
Batch Building
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.
{
"name": "build_model",
"arguments": {
"name": "SIR",
"model_id": "sir",
"sim_specs": {"start": 0, "stop": 100, "dt": 0.125, "time_units": "Days"},
"stocks": [
{"name": "Susceptible", "initial_value": "9999", "units": "people"},
{"name": "Infected", "initial_value": "1", "units": "people"},
{"name": "Recovered", "initial_value": "0", "units": "people"}
],
"auxs": [
{"name": "contact_rate", "equation": "6"},
{"name": "infectivity", "equation": "0.25"},
{"name": "recovery_time", "equation": "2", "units": "days"},
{"name": "total_population", "equation": "Susceptible + Infected + Recovered"}
],
"flows": [
{"name": "infection", "equation": "Susceptible * contact_rate * infectivity * Infected / total_population", "from_stock": "Susceptible", "to_stock": "Infected"},
{"name": "recovery", "equation": "Infected / recovery_time", "from_stock": "Infected", "to_stock": "Recovered"}
],
"modules": [
{"name": "Disease Dynamics", "members": ["Susceptible", "Infected", "Recovered"]}
]
}
}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.
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":"delete_model","arguments":{"model_id":"pop_v1"}}{"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 PopulationReading 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 remineralizationDiagram Preview
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 arcs. 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.
{"name":"render_diagram","arguments":{"model_id":"sir_baseline","filepath":"./sir.svg"}}The diagram below is the built-in sir template rendered by render_diagram
(no manual positioning):
Simulation
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:
pip install 'stella-mcp[sim]'{"name":"simulate","arguments":{"model_id":"pop_v1","overrides":{"growth_rate":0.05},"include":["Population"],"max_points":50}}Notes and caveats:
PySD integrates with Euler only — models whose
methodis RK4 simulate with Euler and the response carries a warning. Results can differ from Stella for stiff systems.PySD supports a subset of XMILE; unsupported constructs fail with a structured error rather than wrong numbers.
overridesaccepts variable names in display ("growth rate") or underscore (growth_rate) form and replaces the variable with a constant.save_results_csvwrites the full-resolution results table with atimecolumn.The session model is never modified by simulation (the run uses a throwaway copy).
Scenario Comparison
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.
{"name":"compare_scenarios","arguments":{"model_id":"pop_v1","include":["Population"],"scenarios":[{"name":"low growth","overrides":{"growth_rate":0.02}},{"name":"high growth","overrides":{"growth_rate":0.08}}]}}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:
Every override name across all scenarios is validated before any run, so a typo fails fast and atomically — no scenario runs half-applied.
A scenario whose run produces NaN/inf reports the warning in that scenario's
warningswithout aborting the others;final_pctisnullwhen the baseline final is zero (no divide-by-zero).baselineis optional — pass an override set to measure deltas against, or omit it to compare against the unmodified model.save_comparison_csvwrites a wide table with one column pervariable__scenario(andvariable__baseline).The compiled model is reused across every scenario in one call, so a comparison is roughly as cheap as a single simulation plus one run per scenario.
Sensitivity Analysis
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.
{"name":"sensitivity_analysis","arguments":{"model_id":"pop_v1","parameters":[{"name":"growth_rate","start":0.02,"stop":0.08,"steps":7}],"output":{"variable":"Population","metric":"final"}}}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:
One-at-a-time only.
modeaccepts"oat"; full-factorial (grid) and Monte-Carlo sampling are reserved for a future release.metricis one offinal,max,min,mean, ortime_to_threshold(which needs anoutput.thresholdand reports the first time the series crosses it). max/min/mean cover finite values only; a non-finite or never-crossing run reportsnullfor that point with a warning.A parameter spec is either
start/stop/steps(evenly spaced,steps≥ 2) or an explicitvalueslist (≥ 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.elasticityisnullwhen it cannot be defined (a non-constant parameter, or a zero baseline metric/parameter);range_sensitivityis still reported.save_sweep_csvwrites a longparameter, value, metrictable.Like scenario comparison, the model is compiled once and reused across the whole sweep.
MCP Resources & Prompts
Beyond tools, the server exposes MCP-native affordances:
Tool annotations. Every tool carries hints (
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.Resources. Templates and session models are readable as resources:
stella://templates/{name}— a built-in or user template's.stmxstella://models/{model_id}— a session model's current XMILE export
Prompt. A
build-stella-modelprompt (argument:description) encodes the recommended build → validate → simulate → render → save workflow, so it is discoverable inside MCP clients.
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)
Units present - A stock or flow missing units while others define them (warning)
Units consistency - A flow whose units don't read as
stock-units/time-unitwhen every attached stock shares the same units (warning; conservative — stays silent on conversion flows and anything it can't confidently parse)Unused auxiliaries - An auxiliary referenced by no equation or connector (warning); stocks and flows are never flagged
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 CIMaintainer 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 logicContributing
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, and move the[Unreleased]items inCHANGELOG.mdunder the new version heading.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
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/bradleylab/stella-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server