Skip to main content
Glama

lsd-mcp

An MCP server that lets an agent work with LSD (Laboratory for Simulation Development, Valente and Pereira): inspect and edit models, compile and run them headless, and run sensitivity analyses (meta-models with Sobol indices, elementary effects). It uses LSD's own engine, command-line utilities (lsd_confgen, lsd_getsaved, ...), design code and R package LSDsensitivity; it does not re-implement them. Built against release tag 8.1-stable-5.

It runs LSD either on the host or, with the Docker backend below, inside the container of Docker_LSD_setup.

Install

Needs Python 3.10+, uv, a C++ compiler (c++, g++ or clang++), zlib, and git (to fetch the LSD source on first use). Rscript with the R package LSDsensitivity is only needed for sa_analyze.

uv sync
uv run pytest -q

Related MCP server: COMSOL MCP Verifier

Environment variables (all optional)

Variable

Default

Meaning

LSDROOT

unset

An LSD folder containing src/. If unset, the tag is fetched (sparse clone of src, Example, Rpkg).

LSD_TAG

8.1-stable-5

Tag to fetch.

LSD_MCP_HOME

~/.cache/lsd-mcp

Fetched source and all build output.

LSD_MODELS

~/lsd-models

Your models. The only place the edit and run tools write.

LSD_MCP_RSCRIPT

Rscript

R interpreter for sa_analyze.

Tools

Inspect: lsd_status, list_models, read_equations, describe_configuration. Edit: copy_model, create_model, write_equations, edit_structure, set_values, set_run_settings, set_saved. Compile and run: compile_model, run_configuration, read_results. Sensitivity analysis: sa_create_design, sa_run_design, sa_analyze. set_values and the factors of sa_create_design take "Name -k" for the k-th lag of a variable.

create_model makes a new model folder as LSD's model manager (LMM) does: the equation file fun_<name>.cpp from LSD's template, model_options.txt, modelinfo.txt, description.txt and a Sim1.lsd holding only Root. edit_structure applies a list of operations (add object, parameter, variable or function, rename, delete, set the number of instances, set the value of each instance, set a description) to a configuration with LSD's own functions, through lsd_edit (src/lsd_mcp/structure.cpp), and saves with LSD's save_configuration; if one operation fails nothing is written. With these, a model can be built from nothing: create_model, edit_structure, write_equations, run_configuration.

sa_create_design makes one of four designs. lhs and random are sampled in Python and written through lsd_confgen. nolh (near-orthogonal Latin hypercube, LSD's own tables, optionally extended) and ee (elementary effects, Morris; trajectories, levels, jump, pool) are made by LSD's own design code (design and sensitivity_doe in set_all.cpp) through lsd_doe, a small program of ours (src/lsd_mcp/doe.cpp) that includes LSD's file unmodified and makes the calls the interface makes. A nolh design gets an out-of-sample set from LSD's Monte Carlo range sampling, as the interface offers after NOLH; an ee design has none. Every design also gets <config>_design.json (method and parameters). sa_analyze fits a Kriging or polynomial meta-model with Sobol indices, or, for an ee design, runs LSD's elementary effects analysis and returns mu, mu_star, sigma, se and p_value per factor. For an ee design made in LSD's interface (no design file) pass metamodel="ee" with its levels and jump.

Register with Claude Code

claude mcp add lsd -- uv run --project /path/to/lsd-mcp lsd-mcp

Add -e LSD_MODELS=/path/to/models to change the models folder.

Docker backend

With LSD_MCP_BACKEND=docker the server still runs on the host, but every tool call is executed inside a running LSD container by the same standard-library code, which lsd-mcp copies into the container (docker cp, re-copied when the source changes). It never starts, stops or removes a container.

The container comes from Docker_LSD_setup, a separate repository that runs LSD in Docker with a browser desktop, R and LSD's R packages. This backend needs nothing installed on the host besides Docker and uv: the compiler, LSD and R are the container's.

Variable

Default

Meaning

LSD_MCP_BACKEND

local

docker forwards tool calls into the container.

LSD_CONTAINER

lsd

Name of the running container.

LSD_MCP_CONTAINER_LSDROOT

/home/lsd/LSD

LSD folder inside the container.

LSD_MCP_CONTAINER_MODELS

/home/lsd/LSD/Work

Models folder inside the container.

claude mcp add lsd-docker -e LSD_MCP_BACKEND=docker -- uv run --project /path/to/lsd-mcp lsd-mcp

Start the container with ./run.sh in your Docker_LSD_setup folder first. Two things to know: models live in the container's Work folder, which is shared with the host and shown in LMM as "Work in Progress" (copy_model adds a modelinfo.txt so LMM lists the copy); and the build cache inside the container is lost when the container is recreated, so the next call rebuilds it.

Limits

  • Kriging can fail with "the leading minor ... is not positive" (covariance matrix not positive definite), typically when design points nearly coincide, for example integer factors with few levels. sa_analyze explains this; try the polynomial meta-model or more spread-out points. sa_create_design warns about integer factors with fewer levels than samples / 4.

  • LSD's polynomial meta-model weights design points by mean/SD of the response and fails when a point has a negative mean (a bug in LSDsensitivity, not worked around here). sa_analyze says so; use metamodel="kriging".

  • LSD's polynomial meta-model also needs at least two factors (its package builds a broken formula for one); sa_analyze refuses it for a single factor.

  • LSD cannot load a configuration with SEED below 1 (27 shipped examples have SEED 0); the tools say so and set_run_settings(seed=1) fixes it.

  • Totals files (.tot.gz) have no header and one row per run; read_results does not read them and names the .res.gz files instead.

  • Sensitivity analysis covers Latin hypercube, uniform random and NOLH designs with a Kriging or polynomial meta-model, and elementary effects designs.

  • Factors are parameters or the initial values of variables ("X" is the first lag, "X -2" the second). One element can be one factor only: LSD's design table names a factor by its label alone. In lsd_confgen's own files a negative lag always means the first lag (confgen.cpp, change_configuration), so the tools send it the positive number it does honour.

  • An elementary effects design made on macOS differs from one made on Linux (including in Docker) for the same seed, because LSD shuffles trajectories with the C++ standard library's shuffle, which differs between libc++ and libstdc++. Both are valid designs. On Linux the design is byte-identical to what LSD's interface writes for the same settings and seed; NOLH and Monte Carlo range designs are identical on both platforms (tests/data/doe_gui holds the interface's files, tests/test_doe.py and the Docker tests compare them).

  • edit_structure saves with LSD's save_configuration, so the file takes LSD's current layout. Of the 151 shipped example configurations LSD can load, 32 come back byte-identical from an empty edit. The rest differ only in layout (no data): descriptions without text were written as "(no description available)" in older files and are now empty, MODELREPORT lost a leading space, an empty EQ_FILE section is added, and the note LSD generates about the initial values of a variable with no lags is dropped (LSD's save_description). If the equation file is not in the model folder LSD would blank the EQUATION name; the tool puts it back. New instances copy the object's first instance. Objects always keep at least one instance.

  • Models made by create_model use LMM's compiler options (SWITCH_CC=-O0 -ggdb3), so they compile without optimisation; edit model_options.txt for speed.

  • NOLH tables cover 1 to 100 factors (17 to 512 points); samples does not apply.

  • Without the Docker backend the compiler and utilities run on the host.

  • Models are compiled headless (-D_NW_). Eight of the 45 example models use GUI-only features (Tcl calls, the debugger variables) and do not compile this way.

  • lsd_confgen in 8.1-stable-5 generates at most as many configurations per call as the CSV has rows; sa_create_design splits the work accordingly.

  • lsd_confgen drops everything after MODELREPORT (descriptions, embedded equations). set_values appends it again from the original file; the numbered design configurations are left as LSD writes them.

  • sa_analyze is tested on Linux with R 4.3.3 and LSDsensitivity 1.2.3 (the tests are skipped where R is missing).

Available Tools

17 tools
compile_modelB

Compile the model's equation file into a headless LSD program (built into the cache, not the model folder). Returns ok and build time, or the first compiler errors as file:line: message. Nothing is rebuilt if the equation file is unchanged.

Inputs: model (required), group.

copy_modelB

Copy a model folder (source files only: no binaries, results, backups, numbered design configurations, design tables or _sa folders) into the models folder as name, so it can be edited. Fails if name exists.

Inputs: name (required), source (required), source_group.

create_modelA

Create a new, empty model folder in the models folder, as LSD's model manager (LMM) does: equation file fun_.cpp from LSD's template, model_options.txt, modelinfo.txt (LMM lists a folder only with it), description.txt, and a configuration Sim1.lsd holding only Root. name is the folder (letters, digits, underscores; it must not exist or lie inside another model). Then use edit_structure to add objects, parameters and variables, write_equations for the equations, set_saved if needed, and run_configuration. An empty model compiles and runs (it computes nothing and saves nothing).

Inputs: name (required), title, description.

describe_configurationA

Describe a .lsd configuration: the object tree with instance counts, and for each element (variable, parameter or function) its type, number of lags, whether it is saved to the result files, and its value (a single value if all instances are equal, otherwise min, max and count), plus the run settings SIM_NUM, SEED, MAX_STEP and EQUATION. config is the file name with or without .lsd. Set only when they apply: computed=false on objects (not computed), and debug, plot, parallel, saved_separately on elements. For big models pass object='Name' to describe one object, or detail='names' for the tree with instance counts and just the element names grouped by type.

Inputs: model (required), config (required), group, detail, +1 more.

edit_structureA

Change a configuration's structure (objects, parameters, variables, functions, instance counts) with LSD's own code. operations is a list of objects applied in order; if any fails (the message names it) nothing is written. Writes new_config.lsd, or replaces config.lsd (old file kept as .bak). Names must be valid LSD labels (letter or '' first, then letters, digits, '') and unique across the whole model, objects and elements alike. Operations, one example each: {"op": "add_object", "parent": "Root", "name": "Firm", "instances": 10} {"op": "add_parameter", "object": "Firm", "name": "alpha", "value": 0.5} {"op": "add_variable", "object": "Firm", "name": "K", "lags": 1, "initial": 1.0, "saved": true} (initial: a number, or one per lag) {"op": "add_function", "object": "Firm", "name": "f"} {"op": "rename", "name": "old", "new_name": "new"} (object or element) {"op": "delete", "name": "K"} (element; an object with elements or child objects needs "force": true and goes with all of it) {"op": "set_instances", "object": "Firm", "instances": 50} {"op": "set_instance_values", "name": "alpha", "values": [0.1, 0.2], "lag": 2} (one value per instance in file order; lag only for variables) {"op": "describe", "name": "alpha", "text": "adjustment speed"} Parameters and variables are added to every instance of the object, with the value (default 0) in each. set_instances gives the object that number under every parent instance; new instances are copies of the object's first instance (its values and its child objects), extra ones are removed from the end; at least 1. Equations are not touched: add or change them with write_equations. LSD's save rewrites the file in its own layout, so old files may change in layout (empty descriptions, the notes LSD generates for initial values of variables with no lags are dropped), never in values.

Inputs: model (required), config (required), operations (required), new_config.

list_modelsA

List models. group='models' is the user's LSD_MODELS folder; group='examples' is the Example folder of the LSD distribution (read-only). A model is a folder with an equation file (fun_*.cpp) and at least one .lsd configuration. Returns relative path, title, equation file, source files (*.cpp, *.h, *.hpp in the folder) and configuration names for each.

Inputs: group. Returns: result

lsd_statusA

Show the setup: LSD source root and tag, models folder, whether a C++ compiler was found, whether LSD's command-line utilities are built yet (they are built automatically on first use), and whether Rscript and the R package LSDsensitivity are available.

Inputs: no named parameters.

read_equationsA

Return the text of the model's equation file (the C++ source with the EQUATION(...) blocks). Models whose equations are spread over several files list them as source_files in list_models; pass such a plain file name (.cpp, .h or .hpp, no path) as file to read it instead.

Inputs: model (required), file, group. Returns: result

read_resultsA

Read time series from one result file (.res.gz) in the model folder. variables filters by name ('Mean') or name with instance ('Mean 1'); start/end select time steps (the row number in the file is the time step; row 0 holds initial values; start must not exceed end; max_points >= 1); the series are thinned evenly to max_points. At most 50 series are returned.

Inputs: model (required), results_file (required), end, start, +2 more.

run_configurationA

Compile if needed and run a configuration of a model in the models folder. seed and runs override SEED and SIM_NUM of the file. threads: with runs > 1, the number of runs executed in parallel; otherwise threads for models that use parallel objects. Results are written next to the configuration as .res.gz. Returns the files written and, for the first run, the last value and mean of each saved series (at most 50 series, those whose name has few instances first; the rest are counted). With runs > 1 (sequential) LSD also writes _.tot.gz; with threads set the runs are parallel and no totals file is written. Row 0 of a result file holds initial values. On failure returns the tail of LSD's output.

Inputs: model (required), config (required), runs, seed, +2 more.

sa_analyzeA

Analyse the design results for one saved variable with LSD's R package LSDsensitivity. metamodel 'kriging' (default for lhs, random and nolh designs) or 'polynomial' fits a meta-model and computes its Sobol decomposition: returns the fit quality (Q2 for kriging, R2 for polynomial) and a table with direct effects and interactions per factor. For a design made with method 'ee' the analysis is elementary effects (metamodel 'ee', chosen automatically): returns per factor mu, mu_star, sigma, se and p_value (parameters scaled to [0, 1]; mu_star is the overall effect, sigma non-linear or interaction effects, p_value tests mu_star = 0), sorted by mu_star. Kriging or polynomial on an ee design, or ee on another design, is an error. For an ee design made in LSD's own interface (no design file) pass metamodel='ee' with its levels and jump. ini_drop drops initial time steps, n_keep keeps that many (-1 = all). The response is the mean of the variable over the kept steps, averaged over runs. Variables whose name starts with '_' work; with several instances only the first instance is analysed. ini_drop must be below MAX_STEP and ini_drop + n_keep at most MAX_STEP. r_seed seeds R's random numbers, so identical calls give identical results. A warning is added when a meta-model fit is below 0.5. The polynomial meta-model fails when a design point has a negative mean response (LSD weights points by mean/SD) and needs at least two factors; use kriging then. Kriging can fail numerically when design points nearly coincide (the message says so; try polynomial). Needs Rscript and LSDsensitivity; says so if they are missing.

Inputs: model (required), config (required), variable (required), jump, +5 more.

sa_create_designA

Create a design of experiments for a sensitivity analysis and write it in LSD's own file layout in the model folder: .sa, the design table _1_N.csv (and for meta-model designs the out-of-sample table _N+1_N+V.csv), numbered configurations _1.lsd ..., and _design.json (our file: method and parameters). factors maps an element to [min, max], or [min, max, "int"] for integers. An element is a parameter name, or a variable name for the variable's initial value at its first lag, or "Name -2", "Name -3" ... (name, space, negative lag) for the initial value at that lag. Functions, variables with no lags and lags beyond the variable's lags are refused, and an element can be a factor only once (LSD's design table names a factor by its label, so the result files and the analysis tables show the plain name, for example "X"; the design file _design.json records each factor's lag, 0 for a parameter). method: 'lhs' (Latin hypercube) or 'random': samples points (required, at least 2) plus validation_samples uniform out-of-sample points, for a Kriging or polynomial meta-model. 'nolh': near-orthogonal Latin hypercube made by LSD's own NOLH tables. LSD chooses the table from the number of factors (17 points for 1-7 factors, 33 for 8-11, 65 for 12-16, 129 for 17-22, 257 for 23-29, 512 for 30-100; extended=True uses LSD's extended size for the table: 33, 65, 129, 257, 257, 512), so samples is not used; the result says how many points LSD produced. Plus validation_samples out-of-sample points by LSD's Monte Carlo range sampling. For meta-model analysis. 'ee': elementary effects (Morris) design made by LSD's own code: trajectories (default 10) each of factors + 1 points, chosen from a pool of pool random trajectories (default 100) on levels levels (even, default 4) with jump (default 2). No out-of-sample set; validation_samples and samples are ignored. An ee design made on macOS differs from one made on Linux for the same seed (the C++ library's shuffle differs); both are valid designs. Analyse it with sa_analyze (metamodel 'ee'). Each point runs runs_per_point times (at least 2) with its own seeds. seed seeds the sampling. Refuses to replace an existing design unless overwrite=True. A warning is returned for integer factors with few levels (a hypercube collapses onto them). Variables whose results will be analysed must be saved (set_saved).

Inputs: model (required), config (required), factors (required), jump, +10 more.

sa_run_designB

Run every numbered configuration of the design in parallel processes (default: one per CPU). Points whose result files already exist are skipped. Returns counts of points done, run, failed and not started.

Inputs: model (required), config (required), threads, timeout_s.

set_run_settingsC

Edit SIM_NUM (runs), SEED and MAX_STEP (time steps) of a configuration.

Inputs: model (required), config (required), runs, seed, +1 more.

set_savedB

Mark the named variables or parameters as saved (or not saved) to the result files. Only saved elements appear in results.

Inputs: model (required), names (required), config (required), saved.

set_valuesA

Set element values in a configuration, using LSD's own lsd_confgen. values maps element name to a number; every instance of the element gets the value. A variable's name sets its initial value at the first lag; "Name -2", "Name -3" ... (name, space, negative lag) set the second, third ... lag ("Name -1" is the first); a lag beyond the variable's lags, or on a parameter, is an error, and one element can appear once per call. Writes new_config.lsd, or replaces config.lsd (the old file is kept as .bak). The result says whether an existing file was replaced (replaced, backup).

Inputs: model (required), config (required), values (required), new_config.

write_equationsA

Replace the model's equation file with content (complete C++ source), or the source file named by file (plain name, .cpp, .h or .hpp; a new file is created if it does not exist). The version before the latest write is kept as .bak and the one before the first write as .orig. Only models in the models folder can be written.

Inputs: model (required), content (required), file.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.6/5.0

    Scored across 17 tools

    Disambiguation4/5

    Most tools have clearly distinct scopes (model management, configuration editing, execution, and sensitivity analysis). Slight overlap exists between set_values and edit_structure's set_instance_values operation, and between set_run_settings and run_configuration's seed/runs overrides, but descriptions distinguish them well.

    Naming Consistency4/5

    Predominantly snake_case verb_noun names such as list_models, read_equations, and create_model. Deviations are the sa_ prefix for sensitivity-analysis tools and lsd_status, but these are readable and consistent within their subgroups.

    Tool Count4/5

    With 17 tools, the set is slightly above the ideal 3-15 range but the domain is broad and each tool covers a distinct workflow step. The count is reasonable, though a couple of configuration-editing tools could potentially be merged.

    Completeness4/5

    Coverage is strong across the model lifecycle (create, copy, read/write equations, edit structure, compile, run) and sensitivity analysis (design, run, analyze). Minor gaps include no delete_model/delete_configuration and no explicit list_results, which agents can partially work around.

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables AI hosts to design and simulate complex adaptive systems, including managing agent types, running simulations, and analyzing emergent behavior, feedback loops, and sensitivities.
      1
      MIT
    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables agents to build, validate, calibrate, and export versioned scientific models with mandatory units, dimensional and numerical checks, and uncertainty analysis over stdio. Models can be simulated and exported to portable formats such as SBML, CellML, and Modelica.
      65 npm
      9
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables AI agents to read, write, and patch game economy and mechanic-map models stored as YAML, then run static analysis, simulation, and balance search on them so agent-driven work stays in sync with models edited in the browser.
      9 npm
      1
      MIT