Skip to main content
Glama

CAS Studio

A studio for designing and simulating complex adaptive systems (CAS) as agent-based models. You define agent types (state + local rules), wire agents into a directed, signed interaction graph, give the system an open environment with sources and sinks, and run a deterministic, seeded, synchronous simulation engine against it. The seven canonical CAS properties are first-class, instrumented features — each has explicit model constructs and analysis endpoints, not just documentation.

Pure Python + numpy core. FastAPI + SQLAlchemy + Alembic for the REST API and persistence. Vanilla-JS canvas UI. No heavy deps, no network calls, no external data.

Quickstart

./run.sh                      # venv + deps (via uv), alembic upgrade, uvicorn
# HOST=127.0.0.1 PORT=8002 ./run.sh   to override

Then open http://localhost:8000/ — on first start against an empty database the seed demo is loaded (and audit-logged): innovation diffusion in a market — 40 agents on a small-world ring lattice with innovators, early-majority and laggard adopters, two skeptics (balancing loops), two adaptive-price vendors, and an information source in the environment. Two adjacent seeded adopters trigger an S-curve adoption cascade (mean adoption 0.12 → 0.95 over ~11 steps) with flagged emergence events at the takeoff; dropping to a single seed makes it fizzle.

Run the tests:

.venv/bin/python -m pytest tests/ -q

Related MCP server: COMSOL MCP Server

The seven CAS properties

1. Emergence

Macro patterns arise from micro interactions. Every step the engine records macro metrics: the mean field of every state var, population variance, active-cluster count (connected components of "active" agents — primary var ≥ 0.5 — over the undirected interaction graph), an order parameter |2·active_fraction − 1|, and a Moran's-I-like neighbor correlation. GET /api/runs/{id}/emergence returns the series plus flagged emergence events: steps where the z-score of a macro indicator's step-to-step delta exceeds 3 while exogenous inputs were constant (no injections). Honest caveat: a z-score over a short series is a blunt detector — treat events as flags for inspection, not proof.

2. Nonlinearity

POST /api/systems/{id}/sensitivity with {"param", "deltas": [...], "steps", "seed"} reruns the simulation with param perturbed by each delta and reports response ratios |Δoutcome/Δparam| (outcome = final mean field of the primary var). It flags nonlinear (ratios vary by >10× across magnitudes — superlinear regime), threshold (some perturbations produce a response, others none), and sign_flip. Parameter addressing:

param form

meaning

env:<var>

environment initial value

flow:<var>:inflow / flow:<var>:outflow_rate

source/sink field

type:<type>:<var>

initial state var of every agent of a type

seed_count:<type>:<var>

how many agents of a type start with <var>=1 (a contiguous block anchored at the current first active agent)

The seed demo has a genuine threshold at seed_count:innovator:adopted: one seed fizzles (adoption 0.075), two adjacent seeds cascade (0.95).

3. Decentralization

Agents act only on local information: their own state, weighted means of their direct in-neighbors' state, and environment variables via flux. The rule DSL's condition vocabulary is closed — global state is unrepresentable by construction (there is no "all agents" aggregate, no global lookup). Self-organization is tracked by the Moran's-I-like neighbor-state correlation in every run's series.

4. Feedback loops

GET /api/systems/{id}/loops enumerates elementary cycles of the directed interaction graph (Johnson-style DFS, each cycle reported once from its smallest node; max_len and a 1000-cycle cap bound the enumeration and report truncated). Each loop is classified reinforcing (even count of negative couplings) or balancing (odd count) with loop gain = product of edge weights. Run summaries include per-loop activity: the edge flux that flowed through each loop's edges during the run, where edge flux per step = |Δ primary var of the source| × |weight| (a heuristic for "how much change propagated along this edge", not a physical quantity).

5. Adaptation

Agent types may declare "adaptation": {"target_var", "target_value", "rate"}. Each step the agent adds a bounded bias to target_var; after the step the bias is updated by gradient-free reinforcement: if the step moved the var closer to target_value, keep and amplify the bias (×1.25, capped at |1.0|), else reverse and damp it (×−0.5). Deterministic given the seed. The seed demo's vendors hill-climb price toward 0.7 this way.

6. Open boundaries

Every system has an environment: named float variables plus sources/sinks — {"var": "information", "inflow": 1.0, "outflow_rate": 0.05} applies v += inflow − outflow_rate·v per step. Agents exchange with the environment through conserving flux effects: {"flux": "information", "by": 0.02} moves 0.02 units from the environment into the agent (a state var of the same name); the environment loses exactly what agents gain (verified by test). POST /api/runs/{id}/inject with {"step", "var", "amount"} schedules an exogenous pulse: it re-runs the base run's system/seed/steps with the pulse added to the environment at that step, and returns the new run plus the outcome delta.

7. Nested hierarchy

A System may have a parent_id; subsystems are full systems with their own agents, rules and runs. GET /api/systems/{id}/rollup aggregates each child system's latest-run macro metrics into the parent's report (agent-count-weighted mean of the children's primary mean fields).

Rule DSL

An agent type has state (dict of float vars) and rules — a list of {"if": <condition>, "then": [<effect>, ...]}. The first matching rule fires; later rules are ignored that step (a rule with "then": [] is an absorbing-state guard).

Conditions:

"always"
{"var": "adopted", "op": ">=", "value": 0.5}                 // own state
{"neighbor_mean": {"var": "adopted"}, "op": ">=", "value": 0.35}  // in-neighbors

op> < >= <= ==. neighbor_mean is the weighted mean Σ wᵢxᵢ / Σ|wᵢ| over in-neighbors that carry the var — so negative-weight (skeptic) neighbors dilute the mean.

Effects:

{"set": "adopted", "value": 1.0}        // set own var
{"adjust": "energy", "by": -0.1}        // add to own var
{"flux": "information", "by": 0.02}     // conserve quantity with the environment

Individual agents may carry a state_override: floats, or {"uniform": [lo, hi]} which is sampled once per run from the run's seeded RNG (np.random.default_rng(seed)) — this is what makes the run seed meaningful. Same model + same seed → identical metric series (tested).

The engine steps synchronously: every agent computes its next state from the same snapshot — no update-order artifacts (tested with an oscillating two-agent model).

REST API

All mutations are audit-logged (GET /api/audit).

Endpoint

Purpose

GET/POST /api/systems, GET/DELETE /api/systems/{id}

system CRUD (parent_id for hierarchy)

POST /api/systems/{id}/agent-types, DELETE /api/agent-types/{id}

agent types (DSL validated on write)

POST /api/systems/{id}/agents, DELETE /api/agents/{id}

agents

POST /api/systems/{id}/interactions, DELETE /api/interactions/{id}

directed signed edges

GET/PUT /api/systems/{id}/environment

environment variables + flows

PATCH /api/agents/{id}/position {pos_x, pos_y}

persist an agent's canvas position (saved by the UI on drag-end)

POST /api/systems/{id}/reset_positions

clear all saved canvas positions in a system (UI "Reset layout")

PUT /api/agents/{id} {name?, state_override?}

edit agent (validated, audit-logged)

PUT /api/interactions/{id} {weight}

edit edge weight (audit-logged)

PUT /api/agent-types/{id}

edit type state/rules/adaptation (DSL-validated)

GET /api/examples, POST /api/examples/{name}

curated example systems, one-click copies

POST /api/agent/chat, POST /api/agent/generateGET /api/agent/jobs/{id}

LLM jobs: submit (202) then poll for progress/result

POST /api/systems/{id}/runs {steps, seed}

run the sim → {run_id}

GET /api/runs/{id}

summary (final mean field, loop activity, final states)

GET /api/runs/{id}/series

per-step macro metrics

GET /api/runs/{id}/emergence

emergence events

POST /api/runs/{id}/inject {step, var, amount}

exogenous pulse → new run

GET /api/systems/{id}/loops

feedback loops, classified

POST /api/systems/{id}/sensitivity

perturbation response analysis

GET /api/systems/{id}/rollup

aggregate children's latest runs

MCP integration

CAS Studio ships an MCP (Model Context Protocol) stdio server so AI hosts (Cursor, Claude Desktop, …) can design and simulate systems directly. It fronts the REST API over HTTP — point it at any running instance with CAS_API_URL (default http://127.0.0.1:8000).

Register it in your host's MCP config (see mcp-config.example.json):

{
  "mcpServers": {
    "cas-studio": {
      "command": "/path/to/cas-studio/.venv/bin/python",
      "args": ["-m", "app.mcp_server"],
      "cwd": "/path/to/cas-studio",
      "env": {"CAS_API_URL": "http://127.0.0.1:8000"}
    }
  }
}

Tools exposed (each proxies the matching REST endpoint above): list_systems, get_system, list_agent_types, create_agent_type, create_agents (batch), add_interaction, set_environment_flow, run_simulation, get_run_series, get_emergence_events, list_feedback_loops, run_sensitivity, inject_pulse, get_rollup, and describe_cas_properties (the seven-property guide + rule DSL).

AI agent

The UI's AI Agent tab is a built-in chat agent that drives the studio through a local Ollama LLM — no API keys, nothing leaves the machine.

Requirements: Ollama running (ollama serve) and at least one model, e.g.:

ollama pull qwen3:8b   # the default; supports tool calling and thinking

The model dropdown lists whatever Ollama reports installed and defaults to qwen3:8b, then any gemma3*, then llama3.1:8b, then the first model (GET /api/agent/models). Point the agent at another Ollama host with OLLAMA_URL (default http://127.0.0.1:11434).

POST /api/agent/chat and POST /api/agent/generate run as background jobs (HTTP 202 {job_id}) so slow local LLMs never hang a request: poll GET /api/agent/jobs/{id} for {status: pending|running|done|error, progress: [...], result|error} — progress entries appear as the loop runs ("round 2: calling run_simulation…", "attempt 2: validation failed: …") and every failure ends in a specific human-readable error. LLM calls use Ollama's native /api/chat with think: false (qwen3's thinking mode is ~10× slower on CPU-bound Ollama and adds nothing to tool calls) and the default round cap is 5.

POST /api/agent/chat {messages, model?, system_id?, temperature?, max_rounds?} runs a tool-calling loop against Ollama's /api/chat: the model picks a tool, the server executes it (through the same tool registry the MCP server uses, app/tools.py), appends the result, and repeats — up to max_rounds — until the model writes a final answer. The job result is {reply, model, tool_trace, rounds}; tool_trace lists every call with its arguments and a trimmed result, and the UI renders it as a collapsible block under each reply. Chain-of-thought (<think> blocks from qwen3) is stripped from replies. If the selected model doesn't support tool calling, the agent falls back to a single-shot no-tools answer with a note; if Ollama is down, the models endpoint degrades gracefully and jobs end with a clear error.

POST /api/agent/generate {description, name?, model?, system_id?, temperature?, max_rounds?} turns plain English into a running CAS model. The LLM (Ollama JSON mode) drafts a system spec — agent types with rule-DSL behavior, agent counts, an interaction topology (ring_lattice / random / small_world), signed edge weights by source type, environment variables with sources/sinks, and optional nested child systems — guided by a compact DSL reference, a few-shot example, and systems-engineering design rules (typology decomposition, at least one reinforcing and one balancing loop, open-boundary flows for conserved quantities, nesting for systems-of-systems). The server validates the spec hard (schema, the full rule-DSL validator, topology materialization); on failure the validation errors are fed back to the model, up to max_rounds attempts (default 3), after which the job ends in error with the collected messages. On success everything is created through the repository (audit-logged as generate_system by ai-agent) and the result carries the new system id, a summary, and the raw spec. The same capability is exposed to MCP hosts as the generate_system_from_description tool (it submits the job and waits), and in the UI as the Generate system mode of the AI Agent tab (with live progress).

LLM settings

The gear button in the AI Agent tab opens settings: temperature (default 0.2) and max tool rounds (default 8) — sent per request as temperature / max_rounds to both /api/agent/chat and /api/agent/generate — plus the active Ollama URL and ollama pull hints. Model, temperature and round choices persist in localStorage.

Editing, examples, and guided UX

The canvas has an edit toolbar (Select / Add agent / Connect / Delete) for manual model building — clicking a node or edge opens an editor in the detail panel (name, state override, edge weight). The Structure tab edits agent types (state schema + rule DSL with inline validation errors) and the environment (variables + source/sink flows). New endpoints: PUT /api/agents/{id}, PUT /api/interactions/{id}, PUT /api/agent-types/{id} (all validated and audit-logged). The Examples tab loads curated systems in one click (GET /api/examples, POST /api/examples/{name}): the innovation-market cascade, a predator–prey meadow, and a two-tier supply network (nested hierarchy) — each with a what-to-watch blurb. A first-run help overlay, per-tab explainer bars, and empty-state hints guide new users; the emergence panel shows the strongest sub-threshold shifts (near_misses on GET /api/runs/{id}/emergence) when no events fire.

Graph canvas

The system graph is fully interactive: drag empty space to pan, mouse-wheel to zoom (anchored at the cursor, with a zoom indicator and a Fit button), drag nodes to rearrange them — positions persist server-side (pos_x/pos_y, saved on drag-end) and auto-layout only fills nodes without a saved position. Edges show direction arrows, green solid / red dashed for positive / negative coupling, and weight labels where they carry information beyond the sign. Nodes are labeled with name

  • primary state value after a run; hovering shows a tooltip with the full agent state; a type legend sits in the corner. The pure geometry/layout helpers live in app/static/graph.js and are unit-tested with node --test (see tests/js/).

Layout

app/
  main.py        FastAPI app, REST API, static UI
  tools.py       shared tool registry (names/schemas/execution) for MCP + agent
  mcp_server.py  MCP stdio server fronting the REST API (CAS_API_URL)
  agent.py       in-app AI agent: Ollama tool-calling loop (OLLAMA_URL)
  jobs.py        in-process background jobs for slow LLM work (submit/poll)
  generate.py    natural-language -> validated CAS system spec -> repository
  examples.py    curated one-click example systems
  db.py          engine + session factory (DATABASE_URL, default sqlite:///./cas_studio.db)
  repository.py  SQLAlchemy models + Repository (single DB access point)
  rules.py       rule DSL validation + evaluation (pure functions)
  engine.py      deterministic seeded synchronous simulation engine
  analysis.py    emergence events, loops, sensitivity, Moran's I
  seed.py        the innovation-diffusion demo model
  static/index.html  canvas UI (graph, run controls, chart, loops/sensitivity/inject panels, AI agent)
  static/graph.js    pure canvas helpers (view transform, force layout, edge geometry)
alembic/         schema migrations
tests/           pytest, one file per concern

License

MIT — © 2026 Vector Stream Systems LLC.

A
license - permissive license
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables AI agents to automate multiphysics simulations in COMSOL Multiphysics, covering model management, geometry building, physics configuration, and results visualization. It supports complex simulation workflows through the MCP protocol and includes integrated knowledge retrieval for documentation and troubleshooting.
    650
    MIT
  • A
    license
    B
    quality
    C
    maintenance
    Enables AI agents to automate COMSOL Multiphysics simulations, including model management, geometry building, physics configuration, meshing, solving, and results visualization through the MCP protocol.
    78
    MIT

View all related MCP servers

Related MCP Connectors

  • Deterministic what-if & scenario simulation for AI agents: projections, sensitivity & break-even.

  • Build, validate, and deploy multi-agent AI solutions from any AI environment.

  • Deterministic reasoning stack for AI agents: simulate, decide & compute, plus cross-domain tools.

View all MCP Connectors

Latest Blog Posts

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/radsilent/cas-studio'

If you have feedback or need assistance with the MCP directory API, please join our Discord server