MCP Tool Reference

BondGraph MCP server

The BondGraph Studio MCP server lives at https://xn--lda.se/api/mcp. It is a JSON-RPC 2.0 endpoint that speaks the MCP Streamable HTTP transport. Every POST must include both headers:

Content-Type: application/json
Accept: application/json, text/event-stream

All ten tools below take JSON arguments and return a single text content item whose text field is a JSON string. The envelope is always:

{
  "jsonrpc": "2.0",
  "id": <your id>,
  "result": {
    "content": [
      { "type": "text", "text": "<JSON string — see each tool below>" }
    ]
  }
}

Decode the content[0].text field with JSON.parse (or jq -r '.result.content[0].text' on the command line) to get the structured payload shown in the response examples below.

Handshake

Every MCP host runs initialize and tools/list first:

curl -sS https://xn--lda.se/api/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize",
       "params":{"protocolVersion":"2025-06-18",
                 "capabilities":{},
                 "clientInfo":{"name":"curl","version":"1"}}}'

curl -sS https://xn--lda.se/api/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'

A complete round trip

The exchange below is the full, literal wire traffic for one tool call — request headers, request body, response headers and response body — so you can reproduce it byte for byte.

Request

POST /api/mcp HTTP/1.1
Host: xn--lda.se
Content-Type: application/json
Accept: application/json, text/event-stream

{
  "jsonrpc": "2.0",
  "id": 7,
  "method": "tools/call",
  "params": {
    "name": "validate_bgif",
    "arguments": {
      "bgif": { "bgifVersion": "1.0.0", "components": [], "systems": [] }
    }
  }
}

Response

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Access-Control-Allow-Origin: *

{
  "jsonrpc": "2.0",
  "id": 7,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\"valid\":true,\"modelId\":\"empty\",\"modelName\":\"Untitled\",\"elementCount\":0,\"bondCount\":0,\"subsystemCount\":0}"
      }
    ]
  }
}

Decoded payload

{
  "valid": true,
  "modelId": "empty",
  "modelName": "Untitled",
  "elementCount": 0,
  "bondCount": 0,
  "subsystemCount": 0
}

Batching and notifications

The endpoint accepts a JSON array of requests and answers with an array of the responses that have an id. Requests without an id are notifications: they are executed and produce no response object. If a whole batch consists of notifications the server replies 202 Accepted with an empty body.

curl -sS https://xn--lda.se/api/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '[{"jsonrpc":"2.0","id":1,"method":"ping"},
       {"jsonrpc":"2.0","id":2,"method":"tools/list"}]'

Error codes

Two different failure channels exist, and a correct client checks both.

1. Protocol errors — error instead of result

The request never reached a tool. The response carries no result field at all:

{
  "jsonrpc": "2.0",
  "id": 7,
  "error": {
    "code": -32602,
    "message": "Unknown tool: simulat"
  }
}
CodeHTTPMeaningHow to fix
-32700400Parse error — the body was not valid JSON.Check quoting/escaping; send Content-Type: application/json.
-32600200Invalid Request — no method field.Send a well-formed JSON-RPC 2.0 object.
-32601200Method not found — unsupported JSON-RPC method.Only initialize, notifications/initialized, ping, tools/list and tools/call exist.
-32602200Invalid params — params.name is not a known tool.Call tools/list and use an exact tool name.
-32000405Method not allowed — you used GET or DELETE.All MCP traffic is POST. OPTIONS returns 204 for CORS preflight.

2. Tool errors — result.isError

The tool ran and threw (bad BGIF, unreachable URL, a solver failure). This is HTTP 200 with a normal result, flagged with isError: true and a plain-text message rather than a JSON payload:

{
  "jsonrpc": "2.0",
  "id": 7,
  "result": {
    "content": [
      { "type": "text", "text": "bond references unknown element id \"R7\"" }
    ],
    "isError": true
  }
}

A third, softer channel exists inside the payload itself: several tools (validate_bgif, validate_library, validate_scene, check_units) report expected, non-exceptional problems as {"valid": false, "error": "…"} or a warnings array in an otherwise successful result. Treat those as data, not failures.

Checklist for a robust client: non-2xx HTTP → transport problem; body.error → protocol problem; body.result.isError → tool threw; otherwise JSON.parse(body.result.content[0].text) and inspect valid / warnings.

Machine-readable schemas (type generation)

Every tool publishes a JSON Schema 2020-12 pair — request input and response output — in one self-contained bundle, together with the JSON-RPC envelope and the error-code table above:

curl -sS https://xn--lda.se/api/public/mcp-schema.json

Add ?tool=simulate to fetch a single tool (plus the shared $defs and error codes). Structured tools also return their outputSchema inline from tools/list, so MCP hosts that support structured output pick it up automatically.

{
  "tools": {
    "simulate": {
      "name": "simulate",
      "input":  { "type": "object", "required": ["bgif"], "properties": { … } },
      "output": { "type": "object", "required": ["status", "time", "variables", "meta"], … },
      "resultEncoding": "json",
      "resultPath": "result.content[0].text"
    }
  },
  "errorCodes": [ { "code": -32602, "name": "InvalidParams", "http": 200, … } ],
  "$defs": { "JsonRpcRequest": …, "JsonRpcResponse": …, "ToolResult": … }
}

resultEncoding tells a generator how to decode result.content[0].text: "json" for structured payloads, "text" for raw documents such as render_svg’s SVG. Generate types with any standard tool:

# TypeScript
curl -sS https://xn--lda.se/api/public/mcp-schema.json?tool=simulate \
  | npx json-schema-to-typescript > simulate.d.ts

# Python
curl -sS https://xn--lda.se/api/public/mcp-schema.json?tool=simulate \
  | datamodel-codegen --input-file-type jsonschema --output simulate.py

Minimal working client

About thirty lines is enough to talk to every tool on this page — no MCP SDK required.

TypeScript / JavaScript (fetch)

const ENDPOINT = "https://xn--lda.se/api/mcp";
let nextId = 1;

async function rpc(method: string, params?: unknown) {
  const res = await fetch(ENDPOINT, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Accept: "application/json, text/event-stream",
    },
    body: JSON.stringify({ jsonrpc: "2.0", id: nextId++, method, params }),
  });
  if (!res.ok) throw new Error(`HTTP ${res.status} ${await res.text()}`);
  const body = await res.json();
  if (body.error) throw new Error(`${body.error.code}: ${body.error.message}`);
  return body.result;
}

/** Call a tool and return its decoded JSON payload. */
async function callTool(name: string, args: Record<string, unknown> = {}) {
  const result = await rpc("tools/call", { name, arguments: args });
  const text = result.content?.[0]?.text ?? "";
  if (result.isError) throw new Error(`${name} failed: ${text}`);
  try {
    return JSON.parse(text);
  } catch {
    return text; // render_svg and export_* return raw text, not JSON
  }
}

// --- usage -------------------------------------------------------------
await rpc("initialize", {
  protocolVersion: "2025-06-18",
  capabilities: {},
  clientInfo: { name: "my-client", version: "1.0.0" },
});

const { tools } = await rpc("tools/list");
console.log(tools.map((t: { name: string }) => t.name));

const { bgif } = await callTool("get_example", { name: "RLC Circuit" });
const run = await callTool("simulate", {
  bgif,
  config: { endTime: 5, stepSize: 0.01, solver: "bdf2" },
  format: "compact",
});
console.log(run.meta.sampleCount, Object.keys(run.variables));

Python (standard library only)

import json, itertools, urllib.request

ENDPOINT = "https://xn--lda.se/api/mcp"
_ids = itertools.count(1)

def rpc(method, params=None):
    payload = {"jsonrpc": "2.0", "id": next(_ids), "method": method}
    if params is not None:
        payload["params"] = params
    req = urllib.request.Request(
        ENDPOINT,
        data=json.dumps(payload).encode(),
        headers={
            "Content-Type": "application/json",
            "Accept": "application/json, text/event-stream",
        },
    )
    with urllib.request.urlopen(req) as res:
        body = json.load(res)
    if "error" in body:
        raise RuntimeError(f"{body['error']['code']}: {body['error']['message']}")
    return body["result"]

def call_tool(name, **args):
    result = rpc("tools/call", {"name": name, "arguments": args})
    text = result["content"][0]["text"]
    if result.get("isError"):
        raise RuntimeError(f"{name} failed: {text}")
    try:
        return json.loads(text)
    except json.JSONDecodeError:
        return text

rpc("initialize", {"protocolVersion": "2025-06-18", "capabilities": {},
                   "clientInfo": {"name": "py", "version": "1.0.0"}})
print([t["name"] for t in rpc("tools/list")["tools"]])

model = call_tool("get_example", name="RLC Circuit")["bgif"]
run = call_tool("simulate", bgif=model,
                config={"endTime": 5, "stepSize": 0.01, "solver": "bdf2"},
                format="compact")
print(run["meta"]["sampleCount"], list(run["variables"]))

Existing MCP hosts

Claude Desktop, Cursor, Codex and other hosts only need the URL — the server is public and takes no authentication:

{
  "mcpServers": {
    "bondgraph": {
      "type": "http",
      "url": "https://xn--lda.se/api/mcp"
    }
  }
}

Simulation tools

simulate

Run a BGIF v1 model with the same solver the editor uses and return time-series results.

Inputs

FieldTypeRequiredDescription
bgifobjectyesBGIF v1 document, either bare ({bgifVersion,components,systems}) or the {bgif,layout} wrapper saved by the editor.
configobjectnoOptional overrides: startTime, endTime, stepSize, solver ("bdf1"|"bdf2"), tolerance.
format"full"|"compact"nocompact drops empty variable arrays from the response.

JSON-RPC template (copyable)

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "simulate",
    "arguments": {
      "bgif": "<BGIF document or {bgif,layout} wrapper>",
      "config": {
        "endTime": 5,
        "stepSize": 0.01,
        "solver": "bdf2"
      },
      "format": "compact"
    }
  }
}

Example request

curl -sS https://xn--lda.se/api/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":10,"method":"tools/call",
 "params":{"name":"simulate","arguments":{
       "bgif": {"bgifVersion":"1.0.0","components":[…],"systems":[…]},
       "config": {"endTime":5,"stepSize":0.01,"solver":"bdf2"},
       "format": "compact"
     }}}'

Example response (parsed content[0].text)

{
  "status": "ok",
  "error": null,
  "time": [0, 0.01, 0.02, …, 5.00],
  "variables": {
    "e_C1": [0, 0.0099, 0.0197, …],
    "f_I1": [0, 0.0001, 0.0004, …]
  },
  "meta": {
    "modelId": "rlc",
    "modelName": "RLC Circuit",
    "variableCount": 2,
    "sampleCount": 501,
    "config": {"startTime":0,"endTime":5,"stepSize":0.01,"solver":"bdf2"},
    "durationMs": 38
  }
}

validate_bgif

Parse a BGIF document and report whether it is structurally valid. Cheap; use before simulating large models.

Inputs

FieldTypeRequiredDescription
bgifobjectyesBGIF v1 document or {bgif,layout} wrapper.

JSON-RPC template (copyable)

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "validate_bgif",
    "arguments": {
      "bgif": "<BGIF document or {bgif,layout} wrapper>"
    }
  }
}

Example request

curl -sS https://xn--lda.se/api/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":11,"method":"tools/call",
 "params":{"name":"validate_bgif","arguments":{"bgif":{"bgifVersion":"1.0.0","components":[],"systems":[]}}}}'

Example response — valid

{
  "valid": true,
  "modelId": "empty",
  "modelName": "Untitled",
  "elementCount": 0,
  "bondCount": 0,
  "subsystemCount": 0
}

Example response — invalid

{
  "valid": false,
  "error": "bond references unknown element id \"R7\""
}

list_examples

List every built-in example model with its description, element count, and default simulation config.

Inputs

None — pass "arguments": {}.

JSON-RPC template (copyable)

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "list_examples",
    "arguments": {}
  }
}

Example response

{
  "examples": [
    {
      "name": "RLC Circuit",
      "description": "Series RLC driven by a step voltage source.",
      "elementCount": 5,
      "defaultSimulation": {"startTime":0,"endTime":5,"stepSize":0.01,"solver":"bdf2"}
    },
    {
      "name": "DC Motor",
      "description": "Armature + rotor + load with gyrator coupling.",
      "elementCount": 8,
      "defaultSimulation": {"startTime":0,"endTime":2,"stepSize":0.001,"solver":"bdf2"}
    }
    /* … */
  ]
}

get_example

Return the full BGIF wrapper for a named example. Feed the bgif field straight into simulate.

Inputs

FieldTypeRequiredDescription
namestringyesExample name as returned by list_examples (case sensitive).

JSON-RPC template (copyable)

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "get_example",
    "arguments": {
      "name": "<example name>"
    }
  }
}

Example request

curl -sS https://xn--lda.se/api/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":12,"method":"tools/call",
 "params":{"name":"get_example","arguments":{"name":"RLC Circuit"}}}'

Example response

{
  "name": "RLC Circuit",
  "bgif": {
    "bgif": {
      "bgifVersion": "1.0.0",
      "components": [ /* SubsystemDef objects */ ],
      "systems":    [ /* root system with elements + bonds */ ]
    },
    "layout": { /* canvas positions */ }
  },
  "defaultSimulation": {"startTime":0,"endTime":5,"stepSize":0.01,"solver":"bdf2"}
}

render_svg

Render a BGIF model as an SVG image styled exactly like the editor canvas.

Inputs

FieldTypeRequiredDescription
bgifobjectyesBGIF v1 document or {bgif,layout} wrapper.
theme"dark"|"light"noColor theme; defaults to dark.
widthnumbernoOutput width in px (optional; auto from layout).
heightnumbernoOutput height in px (optional).
paddingnumbernoPadding around the model in px, default 60.
showPortLabelsbooleannoShow port id labels on subsystem ports.

JSON-RPC template (copyable)

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "render_svg",
    "arguments": {
      "bgif": "<BGIF document or {bgif,layout} wrapper>",
      "theme": "dark",
      "padding": 60,
      "showPortLabels": false
    }
  }
}

Example request

curl -sS https://xn--lda.se/api/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":13,"method":"tools/call",
 "params":{"name":"render_svg","arguments":{
       "bgif": {"bgifVersion":"1.0.0","components":[…],"systems":[…]},
       "theme": "light"
     }}}'

Example response

The content[0].text field is raw SVG markup (<svg …>…</svg>) — extract it with jq -r '.result.content[0].text' > model.svg.

The same renderer is also exposed as a plain HTTP image endpoint at GET /api/public/render.svg?bgif=<urlencoded>(or POST with a JSON body for large models). Useful for <img src> embedding in markdown or docs.

Diagnostics, editing, calibration & symbolic export

These tools complement simulate and target three workflows: understanding a model (analyze_causality, check_units), changing it from an agent loop without resending the whole document (the model-editing primitives), and turning it into something else — a parameter sweep, a calibrated parameter set, a small-signal state-space model, a frequency response, or compilable Modelica source.

analyze_causality

Sequential Causality Assignment (SCAP). Returns per-bond causality, the chosen state variables, any algebraic loops, derivative-causality elements, an estimated DAE index, and an overall well-posedness verdict. validate_bgif accepts analyze:true to embed a condensed summary inline.

Inputs

FieldTypeRequiredDescription
bgifobjectyesBGIF document or { bgif, layout } wrapper.

Example request

curl -sS https://xn--lda.se/api/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":30,"method":"tools/call",
 "params":{"name":"analyze_causality","arguments":{"bgif":<BGIF>}}}'

check_units

Dimensional / domain type-check. Infers a physical domain (electrical, mechanical-translational, mechanical-rotational, fluid, thermal, magnetic, chemical) for every element and bond from labels, optional params.domain overrides, and optional <key>_unit strings (e.g. resistance_unit:"ohm", capacitance_unit:"J/K"). Reports per-bond effort/flow units, bond-domain conflicts, GY misuse, and parameter-unit mismatches. Advisory by default; pass strict:true to promote findings to errors.

Inputs

FieldTypeRequiredDescription
bgifobjectyesBGIF document or wrapper.
strictbooleannoWhen true, promote advisory findings to errors and flip ok to false.

Example request

curl -sS https://xn--lda.se/api/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":31,"method":"tools/call",
 "params":{"name":"check_units","arguments":{"bgif":<BGIF>,"strict":false}}}'

parameter_sweep

Cartesian product of parameter axes over the simulator. Each axis is values:[…] or range:{min,max,n,scale:"linear"|"log"}. reduce picks a per-output scalar (final | max | min | absmax | rms | mean) or "timeseries" returns full traces. Capped at maxRuns (default 200).

Inputs

FieldTypeRequiredDescription
bgifobjectyesBGIF document or wrapper.
parametersobject[]yesArray of {elementId,key,values?|range?} axes.
outputsstring[]yesVariable names to record (e.g. q_C1, e_1_R1).
reducestringnofinal | max | min | absmax | rms | mean | "timeseries".
configobjectnoPartial SimulationConfig overrides.
maxRunsnumbernoSafety cap (default 200).

Example request

curl -sS https://xn--lda.se/api/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":32,"method":"tools/call",
 "params":{"name":"parameter_sweep","arguments":{"bgif":<BGIF>,
       "parameters":[{"elementId":"r1","key":"resistance","range":{"min":50,"max":200,"n":4}}],
       "outputs":["q_C1"],
       "reduce":"final"}}}'

fit_parameters

Derivative-free Nelder-Mead calibration of free parameters to a target {t, series:{name:[…]}}. Supports per-series weights and per-parameter bounds. Returns fitted values and RMS residual.

Inputs

FieldTypeRequiredDescription
bgifobjectyesBGIF document or wrapper.
targetobjectyes{ t:number[], series:{ varName:number[] } } — observed trajectory.
parametersobject[]yesFree parameters: {elementId,key,initial?,min?,max?}.
weightsobjectnoOptional per-series weights.
configobjectnoSimulation config used for each candidate.
maxIterationsnumbernoOptimizer cap (default 200).

Example request

curl -sS https://xn--lda.se/api/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":33,"method":"tools/call",
 "params":{"name":"fit_parameters","arguments":{"bgif":<BGIF>,
       "target":{"t":[0,0.1,0.2],"series":{"q_C1":[0,0.5,0.9]}},
       "parameters":[{"elementId":"r1","key":"resistance","initial":100,"min":1,"max":1000}]}}}'

linearize

Extract a small-signal state-space {A, B?, C?, D?} around an operating point. operatingPoint can be "auto" (settle the model via a short simulation), "initial", or a user-supplied {stateName:value} map. State variables are q_*/p_* (bond-graph displacements / momenta). Pass inputs:[{elementId,key}] (source parameters) for B/D and outputs:[varName] (any DAE variable) for C/D. Assumes DAE index ≤ 1 — use analyze_causality to check.

Inputs

FieldTypeRequiredDescription
bgifobjectyesBGIF document or wrapper.
operatingPoint"auto" | "initial" | objectnoWhere to linearize. Defaults to initial conditions.
inputsobject[]noSource parameters to use as inputs — produces B and D.
outputsstring[]noVariable names to use as outputs — produces C and D.
perturbationnumbernoFinite-difference step (default 1e-6).

Example request

curl -sS https://xn--lda.se/api/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":34,"method":"tools/call",
 "params":{"name":"linearize","arguments":{"bgif":<BGIF>,
       "operatingPoint":"auto",
       "inputs":[{"elementId":"se1","key":"effort","label":"V_in"}],
       "outputs":["q_C1"]}}}'

analyze_frequency

Same inputs as linearize plus an optional frequencies field (rad/s — either a list or {start,stop,points,scale:"log"|"linear"}). Returns poles with damping ratio and natural frequency, a stable verdict, the spectral abscissa, and — when both inputs and outputs are supplied — Bode magnitude (dB) and phase (deg) at each frequency.

Example request

curl -sS https://xn--lda.se/api/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":35,"method":"tools/call",
 "params":{"name":"analyze_frequency","arguments":{"bgif":<BGIF>,
       "operatingPoint":"auto",
       "inputs":[{"elementId":"se1","key":"effort","label":"V_in"}],
       "outputs":["q_C1"],
       "frequencies":{"start":0.1,"stop":1000,"points":40,"scale":"log"}}}}'

export_equations

Symbolic equation export. form:'dae' returns a structured DAE — states with initials, algebraic vars, parameters with units, equations tagged source / junction-equal / junction-balance / constitutive / state-derivative. form:'ode' best-effort reduces to an explicit ODE by forward-substituting algebraic definitions and linearly solving single-unknown junction balances (so RC / RL / RLC-series reduce to plain der(q)=…); any irreducible loops are surfaced in unreducedAlgebraic. form:'modelica' emits compilable Modelica source — parameter docstrings, unit="…" attributes, start= on non-zero states, equations grouped by element, sanitized identifiers, and dialect→Modelica rewrites for PI, step, dead, **, log2, cbrt, round, and t→time. Subsystems are flattened automatically and names use the same alias resolution as the editor and simulator.

Inputs

FieldTypeRequiredDescription
bgifobjectyesBGIF document or wrapper.
form"dae" | "ode" | "modelica"noOutput shape. Default "dae".
modelNamestringnoOverride the Modelica model identifier (sanitized).

Example request — Modelica

curl -sS https://xn--lda.se/api/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":36,"method":"tools/call",
 "params":{"name":"export_equations","arguments":{"bgif":<BGIF>,"form":"modelica","modelName":"MyModel"}}}'

Example response (excerpt) — Modelica

model MyModel
  // Auto-generated from BondGraph Studio.

  // ----- Parameters -----
  // R1
  parameter Real R_R1(unit="ohm") = 100.0 "resistance of R1";
  // C1
  parameter Real C_C1 = 0.001 "capacitance of C1";
  // V_in
  parameter Real E_V_in = 10.0 "effort source of V_in";

  // ----- State variables -----
  Real q_C1 "generalized displacement of C1";

  // ----- Algebraic variables (bond efforts and flows) -----
  Real e_V_in_1; Real f_V_in_1;
  Real e_1_R1;   Real f_1_R1;
  Real e_1_C1;   Real f_1_C1;

equation
  // V_in
  e_V_in_1 = E_V_in;
  // 1-junction 1
  f_1_R1 = f_V_in_1;
  f_1_C1 = f_V_in_1;
  0 = (-e_V_in_1) + e_1_R1 + e_1_C1;
  // R1
  e_1_R1 = R_R1 * f_1_R1;
  // C1
  der(q_C1) = f_1_C1;
  e_1_C1 = q_C1 / C_C1;
end MyModel;

Model-editing primitives

Round-trip editing tools — each takes a BGIF wrapper and returns the updated wrapper, so an agent can mutate a model across many turns without resending the whole document. Use apply_edits when you want several changes to land atomically (it rolls back on first failure).

FieldTypeRequiredDescription
add_elementtoolnoInsert a primitive (R/C/I/Se/Sf/TF/GY/0/1) or subsystem instance.
remove_elementtoolnoDelete an element and every bond touching it.
set_parametertoolnoChange a single params field on an element (including subsystem instance overrides).
rename_elementtoolnoChange an element's label — alias-resolved variable names follow.
connecttoolnoAdd a bond between two (element, port) pairs with an optional causal stroke.
disconnecttoolnoRemove a bond by id.
apply_editstoolnoRun an ordered list of the above as a transaction.

Example request — apply_edits

curl -sS https://xn--lda.se/api/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":37,"method":"tools/call",
 "params":{"name":"apply_edits","arguments":{"bgif":<BGIF>,
       "edits":[
         {"op":"set_parameter","elementId":"r1","key":"resistance","value":220},
         {"op":"rename_element","elementId":"c1","label":"C_out"}
       ]}}}'

3D scene authoring tools

Geometry is a first-class artefact next to the bond graph: preview_scene and animate_scene render and play it back, while these four tools let an agent discover the primitive catalogue, author or repair a scene, check it, and write it out as URDF, MJCF or the native JSON scene format. Every one of them accepts the same geometry sources — a scene (URDF/MJCF XML or JSON), a bgif model with optional geometryHints, or a built-in example bundle.

list_scene_primitives

List the geometric primitives available to the scene editor with their shape, default size vector, size-field meaning and default colour.

Example request

curl -sS https://xn--lda.se/api/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":60,"method":"tools/call",
 "params":{"name":"list_scene_primitives","arguments":{}}}'

validate_scene

Structurally validate a scene: duplicate body/joint names, missing or cyclic parents, zero joint axes, inverted limits, empty or zero-sized geometry, meshes with no file reference.

Example request

curl -sS https://xn--lda.se/api/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":61,"method":"tools/call",
 "params":{"name":"validate_scene","arguments":{"example":"single_joint_arm"}}}'

edit_scene

Apply a batch of declarative edit operations and return the edited scene, per-operation results and fresh validation warnings. Omit every geometry source to build a scene from scratch.

Operations

FieldTypeRequiredDescription
set_scene_nameopnoRename the scene.
add_bodyopnoAdd a body: name?, parent?, primitive?.
remove_bodyopnoRemove a body and re-parent its children.
rename_bodyopnoRename a body (references follow).
update_bodyopnoPatch parent, pos, rpy, axis, jointName, jointType, limit, variable.
set_joint_typeopnofixed | revolute | prismatic, with sensible default limits.
add_geometryopnoAppend a primitive to a body.
remove_geometryopnoDrop geometry by index.
update_geometryopnoPatch shape, size, pos, rpy, color, mesh.

Example request

curl -sS https://xn--lda.se/api/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":62,"method":"tools/call",
 "params":{"name":"edit_scene","arguments":{"ops":[
         {"op":"set_scene_name","name":"crane"},
         {"op":"add_body","name":"base","primitive":"plate"},
         {"op":"add_body","name":"arm","parent":"base","primitive":"rod"},
         {"op":"set_joint_type","name":"arm","jointType":"revolute"},
         {"op":"update_body","name":"arm","patch":{"axis":[0,1,0],"jointName":"shoulder"}}
       ]}}}'

export_scene

Serialise a scene to URDF (ROS), MJCF (MuJoCo) or the native JSON scene format, returning { format, filename, content, warnings }.

Example request

curl -sS https://xn--lda.se/api/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":63,"method":"tools/call",
 "params":{"name":"export_scene","arguments":{"example":"single_joint_arm","format":"urdf"}}}'

Gazebo export (SDFormat)

export_sdf emits an SDFormat model that Gazebo (gz-sim) loads directly, so Gazebo itself becomes the simulator and viewer. It reuses the mechanical mapping of export_physics_xml — a mechanical I becomes a link, the junction it shares motion with becomes the joint DOF, a C on that junction becomes the joint spring, R becomes damping, and Se/Sf mark the actuated DOFs — and the same geometryHints. What SDF adds over URDF:

  • Real inertia tensors computed from the resolved primitive and its mass (box, sphere, cylinder, capsule), with the comOffset written as the inertial pose. For a rotational DOF the I value is an inertia in kg·m² and replaces the geometry-derived component about the joint axis.
  • Joint dynamics from R elements: the resistance becomes <damping>, while explicit friction, coulomb or stiction parameters on the same R become <friction> (N or N·m). The compliance becomes <spring_stiffness> natively — no controller workaround needed.
  • Gravity worked out from the graph itself: a constant effort source that is named like a weight, carries a g parameter, or supplies roughly m·g becomes the world gravity, and every link is marked <gravity>true</gravity>. A model with no such source gets gravity switched off, so Gazebo never adds a load the bond graph never simulated. The same numbers appear in the Physics panel of the geometry view.
  • Contact surfaces: every collision carries <surface><friction> coefficients, plus an optional restitution for bouncing contacts.

export_sdf

Export the mechanical structure as an SDFormat model for Gazebo, with computed inertials, joint damping/friction/spring from R and C elements, and contact surface friction.

Inputs

FieldTypeRequiredDescription
bgifobjectnoRequired. BGIF v1 document.
sdfVersionstringnoSDFormat version on the root element (default "1.9").
fixedBasebooleannoEmit a fixed joint from Gazebo's world to the base link (default true).
baseLinkNamestringnoName of the anchor link (default "base_link").
surfaceFrictionnumber | [mu, mu2]noContact friction coefficients (default 1).
restitutionnumbernoContact bounciness 0..1; omitted when unset.
selfCollidebooleannoAllow links of this model to collide (default false).
rotationalMassnumbernoMass assigned to rotational links, whose I value is an inertia (default 1 kg).
gravitynumber | [x,y,z]noWorld gravity. Omit to infer it from weight-like effort sources; 0 marks the links gravity-free.
geometryHintsarraynoSame per-body pose/shape hints as export_physics_xml; they drive the inertia tensor too.
defaultGeometryobjectnoHint applied to every body before its specific hint.
bodySize / autoLayout / autoLayoutSpacingnumber / boolean / numbernoPlaceholder size and automatic chain spacing for unhinted bodies.

Response

FieldTypeRequiredDescription
xmlstringnoSDFormat document text, ready to drop into a Gazebo model directory.
filenamestringnoSuggested file name, e.g. "pendulum.sdf".
linksarrayno{ name, elementLabel, mass, inertia{ixx,iyy,izz}, com, inertiaSource }.
jointsarrayno{ name, type, parent, child, axis, damping, friction, springStiffness }.
bodiesarraynoThe full mechanical mapping, identical to export_physics_xml.
gravityobjectno{ vector, magnitude, source: option|inferred|none, enabled, note, bodies[] } — the gravity the model already assumes.
mapping / warningsstring[]noHuman-readable mapping log and non-fatal diagnostics.

Example request

curl -sS https://xn--lda.se/api/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":64,"method":"tools/call",
 "params":{"name":"export_sdf","arguments":{"bgif":<BGIF>,
        "surfaceFriction":[0.9,0.9],
        "restitution":0.4,
        "geometryHints":[{"target":"arm","shape":"cylinder","size":[0.04,0.6],
                          "axis":[0,1,0],"pos":[0,0,1.0]}]}}}'

Runnable Gazebo package

export_gazebo_model wraps the SDF in everything gz-sim needs to run it: the model directory (model.sdf, model.config), a world with the inferred gravity, the chosen physics engine and step size, the standard gz-sim plugins and a joint-state publisher, an optional URDF of the same mechanism, a README, the launch commands, and a parity table pairing each joint with the variable the 3D view animates so the Gazebo run can be compared against the bond graph run. The same package is browsable and downloadable on the Gazebo page.

export_gazebo_model

Build a runnable gz-sim package — model.sdf, model.config, world file, optional URDF, README, launch commands and a physics parity table.

Inputs

FieldTypeRequiredDescription
bgifobjectnoRequired. BGIF v1 document.
packageNamestringnoModel directory name (default derived from the model name).
worldNamestringnoWorld name and world file stem (default "bondgraph_world").
physicsEngine"dart" | "bullet-featherstone" | "tpe"noPhysics engine the world requests (default "dart").
stepSizenumbernoMax physics step size in seconds (default 0.001).
groundPlanebooleannoInclude a ground plane and sun in the world (default true).
includeUrdfbooleannoAlso emit model.urdf for ROS tooling (default true).
pausedbooleannoStart gz-sim paused.
…export_sdf options—noEvery export_sdf option (gravity, geometryHints, surfaceFriction, …) is accepted and forwarded.

Response

FieldTypeRequiredDescription
filesarrayno{ path, language, description, contents } for every file in the package.
commandsarrayno{ label, command, description } — gz sim, gz sdf validation and ROS 2 bridging.
parityarraynoPer joint: mass, damping, friction, spring, the 3D-view variable and the gz joint-state topic to compare against.
gravity / sdf / urdfobjectnoThe resolved gravity plus the full export_sdf (and URDF) result.
checks / warningsstring[]noWhat to verify after launching, and non-fatal diagnostics.

Example request

curl -sS https://xn--lda.se/api/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":65,"method":"tools/call",
 "params":{"name":"export_gazebo_model","arguments":{"bgif":<BGIF>,"packageName":"pendulum","physicsEngine":"dart","stepSize":0.0005}}}'

Library discovery & validation tools

Component libraries are bundled in the BGLIB v1 wire format. These five tools let an agent browse the libraries that ship with BondGraph Studio, fetch one a user linked, sanity-check a candidate file, and assemble a fresh library from raw components.

list_libraries

List the curated component libraries shipped with BondGraph Studio (Buildings & HVAC, Power & Energy, Automotive, Hydraulics, Data Center) with a per-component summary.

Inputs

None — pass "arguments": {}.

JSON-RPC template (copyable)

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "list_libraries",
    "arguments": {}
  }
}

Example request

curl -sS https://xn--lda.se/api/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":20,"method":"tools/call",
 "params":{"name":"list_libraries","arguments":{}}}'

Example response

{
  "bglibVersion": "1.0.0",
  "libraries": [
    {
      "name": "Buildings & HVAC",
      "description": "Thermal zones, HVAC, building envelopes",
      "componentCount": 12,
      "components": [
        {
          "id": "thermal-zone",
          "name": "Thermal Zone",
          "group": "Buildings",
          "portCount": 2,
          "description": "Single-capacitance lumped zone with envelope loss."
        },
        /* … */
      ]
    },
    {
      "name": "Power & Energy",
      "description": "Generators, transmission, storage, loads",
      "componentCount": 9,
      "components": [ /* … */ ]
    }
    /* Automotive, Hydraulics, Data Center … */
  ]
}

get_library

Return the full BGLIB v1 document for a shipped library, ready to drop into the editor's Import Library → File dialog.

Inputs

FieldTypeRequiredDescription
namestringyesLibrary name from list_libraries (e.g. "Hydraulics").

JSON-RPC template (copyable)

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "get_library",
    "arguments": {
      "name": "<library name>"
    }
  }
}

Example request

curl -sS https://xn--lda.se/api/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":21,"method":"tools/call",
 "params":{"name":"get_library","arguments":{"name":"Hydraulics"}}}'

Example response

{
  "bglibVersion": "1.0.0",
  "name": "Hydraulics",
  "version": "1.0.0",
  "author": "BondGraph Studio",
  "description": "Pumps, valves, cylinders, accumulators",
  "license": "MIT",
  "bgif": {
    "bgifVersion": "1.0.0",
    "components": [ /* SubsystemDef objects in BGIF form */ ],
    "systems": []
  }
}

validate_library

Parse a candidate BGLIB v1 document (or a legacy BGIF library) and report whether it is valid, the metadata, and the component count — or the exact parser error.

Inputs

FieldTypeRequiredDescription
libraryobjectyesA BGLIB v1 document, or a BGIF document / wrapper containing components.

JSON-RPC template (copyable)

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "validate_library",
    "arguments": {
      "library": "<BGLIB v1 document or BGIF wrapper>"
    }
  }
}

Example request

curl -sS https://xn--lda.se/api/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":22,"method":"tools/call",
 "params":{"name":"validate_library","arguments":{
       "library": {
         "bglibVersion": "1.0.0",
         "name": "Acme Pneumatics",
         "version": "0.3.1",
         "author": "Acme Engineering",
         "bgif": {"bgifVersion":"1.0.0","components":[…],"systems":[]}
       }
     }}}'

Example response — valid

{
  "valid": true,
  "legacy": false,
  "meta": {
    "name": "Acme Pneumatics",
    "version": "0.3.1",
    "author": "Acme Engineering",
    "description": null,
    "license": null,
    "homepage": null
  },
  "componentCount": 7
}

Example response — invalid

{
  "valid": false,
  "error": "BGLIB.bgif.components[2]: missing required field \"ports\""
}

Example response — legacy BGIF accepted

{
  "valid": true,
  "legacy": true,
  "meta": {
    "name": "imported.bgif",
    "version": "0.0.0",
    "author": null,
    "description": "Imported from legacy BGIF document",
    "license": null,
    "homepage": null
  },
  "componentCount": 4
}

fetch_library

Download a BGLIB v1 (or legacy BGIF) document from an HTTPS URL — GitHub raw, gist, S3, a personal site — and return its parsed metadata and component summary.

Inputs

FieldTypeRequiredDescription
urlstring (https://)yesHTTPS URL pointing to a .bglib.json / .bgif / .json file.
summarybooleannoIf true (default) return only metadata + component summary; if false also include the rebuilt BGLIB document.

JSON-RPC template (copyable)

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "fetch_library",
    "arguments": {
      "url": "<https URL to .bglib.json or .bgif>",
      "summary": true
    }
  }
}

Example request

curl -sS https://xn--lda.se/api/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":23,"method":"tools/call",
 "params":{"name":"fetch_library","arguments":{"url":"https://example.com/pumps.bglib.json"}}}'

Example response (summary)

{
  "meta": {
    "name": "Centrifugal Pumps",
    "version": "1.2.0",
    "author": "Jane Doe",
    "description": "Single- and multi-stage centrifugal pumps.",
    "license": "Apache-2.0",
    "homepage": "https://example.com/pumps"
  },
  "legacy": false,
  "componentCount": 5,
  "components": [
    {
      "id": "centrifugal-pump-1stage",
      "name": "Single-stage centrifugal pump",
      "group": "Pumps",
      "portCount": 2,
      "description": "Polynomial head curve, parameterised by rated flow + head."
    }
    /* … */
  ]
}

Example response (summary: false)

{
  "meta": { /* same as above */ },
  "legacy": false,
  "componentCount": 5,
  "components": [ /* same summary */ ],
  "document": {
    "bglibVersion": "1.0.0",
    "name": "Centrifugal Pumps",
    "version": "1.2.0",
    "author": "Jane Doe",
    "bgif": { "bgifVersion": "1.0.0", "components": [ /* full */ ], "systems": [] }
  }
}

Example response — fetch error

{
  "jsonrpc": "2.0",
  "id": 23,
  "error": {
    "code": -32000,
    "message": "fetch_library: HTTP 404 from https://example.com/pumps.bglib.json"
  }
}

build_library

Wrap one or more BGIF subsystem components into a fresh BGLIB v1 document with the given metadata. Use when an agent has authored components and wants to give the user a single shareable file.

Inputs

FieldTypeRequiredDescription
meta.namestringyesHuman-readable library name.
meta.versionstringnoSemver string; defaults to 1.0.0.
meta.authorstringnoAuthor / maintainer name.
meta.descriptionstringnoOne-line description.
meta.licensestringnoSPDX identifier, e.g. MIT.
meta.homepagestring (https://)noProject homepage.
componentsobject[]yesArray of BGIF SubsystemDef components.

JSON-RPC template (copyable)

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "build_library",
    "arguments": {
      "meta": {
        "name": "<library name>",
        "version": "1.0.0",
        "author": "<author>",
        "license": "MIT"
      },
      "components": [
        "<SubsystemDef object 1>",
        "<SubsystemDef object 2>"
      ]
    }
  }
}

Example request

curl -sS https://xn--lda.se/api/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":24,"method":"tools/call",
 "params":{"name":"build_library","arguments":{
       "meta": {
         "name": "My Pumps",
         "version": "0.1.0",
         "author": "Me",
         "license": "MIT"
       },
       "components": [ /* SubsystemDef objects */ ]
     }}}'

Example response

{
  "bglibVersion": "1.0.0",
  "name": "My Pumps",
  "version": "0.1.0",
  "author": "Me",
  "license": "MIT",
  "bgif": {
    "bgifVersion": "1.0.0",
    "components": [ /* echoed back, normalised */ ],
    "systems": []
  }
}

Example BGLIB payload

Below is a complete, copyable BGLIB v1 document containing a single component — a Thermal Zone subsystem. You can paste this directly into the validate_library or build_library tools, or save it as a .bglib.json file and import it into the editor via Import Library → File.

{
  "bglibVersion": "1.0.0",
  "name": "Demo Building Library",
  "version": "1.0.0",
  "author": "BondGraph Studio",
  "description": "A minimal example library with one thermal-zone component.",
  "license": "MIT",
  "bgif": {
    "bgifVersion": "1.0.0",
    "components": [
      {
        "id": "bld_thermal_zone",
        "library": "Demo Building Library",
        "group": "Zones",
        "name": "Thermal Zone",
        "description": "Single-air-node thermal zone with lumped thermal capacitance. Ports: envelope (left), gains (right), adjacent (bottom).",
        "ports": [
          { "id": "env",  "side": "left",  "offset": 0.5 },
          { "id": "gain", "side": "right", "offset": 0.5 },
          { "id": "adj",  "side": "bottom", "offset": 0.5 }
        ],
        "svgIcon": "<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><rect x="15" y="20" width="70" height="60" rx="4" fill="none" stroke="oklch(0.78 0.13 200)" stroke-width="2"/><text x="50" y="50" text-anchor="middle" dominant-baseline="central" font-family="monospace" font-size="14" fill="oklch(0.78 0.13 200)" font-weight="600">T_z</text></svg>",
        "innerElements": [
          {
            "id": "j",
            "type": "0",
            "label": "0",
            "x": 200,
            "y": 160,
            "width": 50,
            "height": 50,
            "ports": [
              { "id": "p1", "side": "top",    "offset": 0.5 },
              { "id": "p2", "side": "right",  "offset": 0.5 },
              { "id": "p3", "side": "bottom", "offset": 0.5 },
              { "id": "p4", "side": "left",   "offset": 0.5 }
            ],
            "params": {}
          },
          {
            "id": "Cz",
            "type": "C",
            "label": "C_zone",
            "x": 200,
            "y": 60,
            "width": 80,
            "height": 50,
            "ports": [
              { "id": "p1", "side": "left",   "offset": 0.5 },
              { "id": "p2", "side": "right",  "offset": 0.5 },
              { "id": "p3", "side": "top",    "offset": 0.5 },
              { "id": "p4", "side": "bottom", "offset": 0.5 }
            ],
            "params": { "capacitance": 5000000, "q0": 90000000 }
          }
        ],
        "innerBonds": [
          {
            "id": "b1",
            "fromElementId": "j",
            "fromPortId": "p1",
            "toElementId": "Cz",
            "toPortId": "p4",
            "causalStroke": "none"
          }
        ],
        "portMap": {
          "env":  { "elementId": "j", "portId": "p4" },
          "gain": { "elementId": "j", "portId": "p2" },
          "adj":  { "elementId": "j", "portId": "p3" }
        }
      }
    ],
    "systems": []
  }
}
See the API section of the main docs for transport details, MCP client configuration snippets, and the direct HTTP fallback at /api/public/simulate.