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-streamAll 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"
}
}| Code | HTTP | Meaning | How to fix |
|---|---|---|---|
-32700 | 400 | Parse error — the body was not valid JSON. | Check quoting/escaping; send Content-Type: application/json. |
-32600 | 200 | Invalid Request — no method field. | Send a well-formed JSON-RPC 2.0 object. |
-32601 | 200 | Method not found — unsupported JSON-RPC method. | Only initialize, notifications/initialized, ping, tools/list and tools/call exist. |
-32602 | 200 | Invalid params — params.name is not a known tool. | Call tools/list and use an exact tool name. |
-32000 | 405 | Method 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.
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.jsonAdd ?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.pyMinimal 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
| Field | Type | Required | Description |
|---|---|---|---|
bgif | object | yes | BGIF v1 document, either bare ({bgifVersion,components,systems}) or the {bgif,layout} wrapper saved by the editor. |
config | object | no | Optional overrides: startTime, endTime, stepSize, solver ("bdf1"|"bdf2"), tolerance. |
format | "full"|"compact" | no | compact 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
| Field | Type | Required | Description |
|---|---|---|---|
bgif | object | yes | BGIF 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
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Example 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
| Field | Type | Required | Description |
|---|---|---|---|
bgif | object | yes | BGIF v1 document or {bgif,layout} wrapper. |
theme | "dark"|"light" | no | Color theme; defaults to dark. |
width | number | no | Output width in px (optional; auto from layout). |
height | number | no | Output height in px (optional). |
padding | number | no | Padding around the model in px, default 60. |
showPortLabels | boolean | no | Show 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
| Field | Type | Required | Description |
|---|---|---|---|
bgif | object | yes | 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":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
| Field | Type | Required | Description |
|---|---|---|---|
bgif | object | yes | BGIF document or wrapper. |
strict | boolean | no | When 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
| Field | Type | Required | Description |
|---|---|---|---|
bgif | object | yes | BGIF document or wrapper. |
parameters | object[] | yes | Array of {elementId,key,values?|range?} axes. |
outputs | string[] | yes | Variable names to record (e.g. q_C1, e_1_R1). |
reduce | string | no | final | max | min | absmax | rms | mean | "timeseries". |
config | object | no | Partial SimulationConfig overrides. |
maxRuns | number | no | Safety 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
| Field | Type | Required | Description |
|---|---|---|---|
bgif | object | yes | BGIF document or wrapper. |
target | object | yes | { t:number[], series:{ varName:number[] } } — observed trajectory. |
parameters | object[] | yes | Free parameters: {elementId,key,initial?,min?,max?}. |
weights | object | no | Optional per-series weights. |
config | object | no | Simulation config used for each candidate. |
maxIterations | number | no | Optimizer 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
| Field | Type | Required | Description |
|---|---|---|---|
bgif | object | yes | BGIF document or wrapper. |
operatingPoint | "auto" | "initial" | object | no | Where to linearize. Defaults to initial conditions. |
inputs | object[] | no | Source parameters to use as inputs — produces B and D. |
outputs | string[] | no | Variable names to use as outputs — produces C and D. |
perturbation | number | no | Finite-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
| Field | Type | Required | Description |
|---|---|---|---|
bgif | object | yes | BGIF document or wrapper. |
form | "dae" | "ode" | "modelica" | no | Output shape. Default "dae". |
modelName | string | no | Override 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).
| Field | Type | Required | Description |
|---|---|---|---|
add_element | tool | no | Insert a primitive (R/C/I/Se/Sf/TF/GY/0/1) or subsystem instance. |
remove_element | tool | no | Delete an element and every bond touching it. |
set_parameter | tool | no | Change a single params field on an element (including subsystem instance overrides). |
rename_element | tool | no | Change an element's label — alias-resolved variable names follow. |
connect | tool | no | Add a bond between two (element, port) pairs with an optional causal stroke. |
disconnect | tool | no | Remove a bond by id. |
apply_edits | tool | no | Run 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
| Field | Type | Required | Description |
|---|---|---|---|
set_scene_name | op | no | Rename the scene. |
add_body | op | no | Add a body: name?, parent?, primitive?. |
remove_body | op | no | Remove a body and re-parent its children. |
rename_body | op | no | Rename a body (references follow). |
update_body | op | no | Patch parent, pos, rpy, axis, jointName, jointType, limit, variable. |
set_joint_type | op | no | fixed | revolute | prismatic, with sensible default limits. |
add_geometry | op | no | Append a primitive to a body. |
remove_geometry | op | no | Drop geometry by index. |
update_geometry | op | no | Patch 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"}}}'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
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Library 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
| Field | Type | Required | Description |
|---|---|---|---|
library | object | yes | A 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
| Field | Type | Required | Description |
|---|---|---|---|
url | string (https://) | yes | HTTPS URL pointing to a .bglib.json / .bgif / .json file. |
summary | boolean | no | If 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
| Field | Type | Required | Description |
|---|---|---|---|
meta.name | string | yes | Human-readable library name. |
meta.version | string | no | Semver string; defaults to 1.0.0. |
meta.author | string | no | Author / maintainer name. |
meta.description | string | no | One-line description. |
meta.license | string | no | SPDX identifier, e.g. MIT. |
meta.homepage | string (https://) | no | Project homepage. |
components | object[] | yes | Array 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": []
}
}