From simulator
Manages business process graphs, flowcharts, and actor-link structures in Simulator.Company. Supports creating, editing, querying nodes/edges and layers.
How this skill is triggered — by the user, by Claude, or both
Slash command
/simulator:simulator-graphThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> **Curated tool names (v2 server).** Place/remove nodes & edges on a layer with `manageLayerActors`; read a layer's contents — prefer `getLayerActorsPaginated` (page nodes then edges; works for any size) or `getAllLayerPlacements`, falling back to `getLayerActors` only for small layers (it loads the whole layer in one call and is rejected with a "Layer is too large" 400 above the size cap); cr...
Curated tool names (v2 server). Place/remove nodes & edges on a layer with
manageLayerActors; read a layer's contents — prefergetLayerActorsPaginated(page nodes then edges; works for any size) orgetAllLayerPlacements, falling back togetLayerActorsonly for small layers (it loads the whole layer in one call and is rejected with a "Layer is too large" 400 above the size cap); create nodes withcreateActor(one call each — there is nocreateActors); links withcreateLink/massLink; edge CRUD withgetEdge/updateEdge/deleteEdge/existLink/deleteEdgesByNodes; edge types withgetEdgeTypes. Traverse from an actor withgetRelatedActors(type = linked | parents | children; hierarchy link type by default; paginated/filterable/sortable),getLinkedActors(directly-linked actors across edge types, withedgeTypes/withSystem/pinnedfilters), andgetActorLinks(every edge of an actor). Layer ops:layerStats(node/edge counts),existLayerElement(is a node/edge on a layer — dedup before placing),moveActors(move ≤10 actors between layers),cleanGraphLayer(wipe a layer — destructive). See/simulatorfor the full list.
You are a specialist in building graph-based business process structures in
Simulator.Company using the simulator MCP server.
Reading "the nodes on a graph/layer"? Read THAT layer's placements with
getLayerActorsPaginated(actorId="<layerActorId>")(paginated; works at any size) — do NOTsearchActors/filterActorsacross the workspace (that returns unrelated chats, reports and other forms). When the user pastes a URL like.../graph/<graphActorId>/layers/<layerActorId>, the layeractorIdis the last UUID, the segment after/layers/. See Layer Operations below for the full recipe.
| Term | Description |
|---|---|
| Actor | Graph node. Created from a Form template. Has id, title, color, data fields. |
| Form | Actor template/type. Defines shape and behavior. All system form names are in the catalog below. |
| Graph actor | An actor with formName="Graphs" — the logical container for a diagram. |
| Layer actor | An actor with formName="Layers" — the visual canvas where nodes are placed at (x, y). |
| Graph file | A YAML file named <layerId>.yaml in the current working directory describing the full layer state. |
| laId | Layer Actor ID. Assigned by manageLayerActors when an actor is placed on a layer. |
This is the preferred approach for all graph creation and editing. Use the low-level MCP tools (createActor, manageLayerActors, etc.) only for one-off queries or when specifically requested.
Every diagram needs two actors linked together. Do this once per new diagram.
// 1. Create the graph container
createActor(formName="Graphs", title="<diagram name>")
→ save returned id as graphId
// 2. Create the visual canvas (layer)
createActor(formName="Layers", title="<diagram name>")
→ save returned id as layerId
// 3. Link graph → layer
createLink(source="<graphId>", target="<layerId>")
If layerId is already known (from user message or context) — skip this step entirely.
Option A — New empty layer: write <layerId>.yaml from scratch:
layerId: "<layerId>"
actors:
- id: start
title: "Start"
formName: "Start / Stop"
color: "#17B26A"
position:
x: 0
y: 0
- id: process1
title: "Process Step"
formName: "Process"
color: "#539fdf"
description: "Does something important"
position:
x: 0
y: 130
- id: end
title: "End"
formName: "Start / Stop"
color: "#F04438"
position:
x: 0
y: 260
edges:
- source: start
target: process1
- source: process1
target: end
Option B — Existing layer: pull current state into a file, then edit it:
pullGraphFile(layerId="<layerId>")
→ creates <layerId>.yaml with all current actors and edges
→ open and edit the file as needed
pushGraphFile(layerId="<layerId>")
The server will:
id is a local name (e.g. start, process1) → replaces local ids with server UUIDs in the
fileid is already a UUID — if title/color/description changedposition changedAfter push the file is updated in place with all server UUIDs.
// 1. Pull current state
pullGraphFile(layerId="<layerId>")
// 2. Edit <layerId>.yaml — add/remove/modify actors and edges
// 3. Push changes
pushGraphFile(layerId="<layerId>")
An edge placement's data.layerSettings controls how the line renders, applied when
you place the edge via manageLayerActors:
manageLayerActors(actorId="<layerId>", items=[
{ action:"create", data:{ id:"<edgeId>", type:"edge", laIdSource:<laA>, laIdTarget:<laB>,
layerSettings:{ lineStyle:"dashed", color:"#E8924E", width:2, curveStyle:"straight" } } }
])
lineStyle — solid | dashed | dottedcurveStyle — curved | rounded | roundedDownward | straightcolor — 6-digit hex #RRGGBB (e.g. #9AA5B1 grey, #E8924E orange) — no 3-digit shorthand, no alphawidth — stroke width, an integer ≥ 1 (e.g. 2)routingPoints — optional array of { w:number, d:number } waypoints for manual edge routingThese are the pong-server edge layerSettings keys (saveEdgeLayerSettingsSchema); on
manageLayerActors they ride through unvalidated, so use the canonical types above — an integer
width and a 6-digit hex color. Use it to encode meaning in edges — coloured solid = data/structure
flows, grey dashed = logical cross-links. To change an edge already on the layer, delete its placement
and re-create it with the new layerSettings (re-creating without deleting first adds a duplicate).
actors.dataWhen the user specifies a custom formId (or a non-system formName) for one or more actors, you must fetch the form schema before writing the YAML file or pushing to the server.
formId UUID for an actor.// 1. Fetch form schema
getForm(formId="<formId>")
→ returns form object with fields list
// 2. Inspect the returned fields array — each field has:
// { id, name, title, type, defaultValue, ... }
// 3. Build actors.data from those fields:
// - Include every field that the user provided a value for.
// - For fields the user did NOT mention: include the field with its
// defaultValue if one exists; omit the field entirely otherwise.
// - Use the field's `name` (not `title`) as the key in data.
// 4. Write the actor entry in the YAML with the populated data block.
actors:
- id: my_actor
title: "My Custom Actor"
formId: "abc-123-uuid" # custom form — data was populated from getForm
color: "#539fdf"
position:
x: 0
y: 130
data:
status: "active" # field name from form schema, value from user
priority: 1 # field name from form schema, value from user
category: "" # field present in schema, no default, user left blank
getForm before writing data when a custom formId is involved — never guess field names.data entirely.data for system forms (those in the Form Catalog) — the server auto-injects their shape/view/blockId.defaultValue; omit fields with no default and no user value.getForm, confirm the field list with the user if the form has many fields and the user has not specified values — ask which fields matter.Render an actor's description as borderless text (no node circle) by placing it with
isTextNode in its data.layerSettings on manageLayerActors:
createActor(formId=3279, description="Section title") // the text lives in `description`
manageLayerActors(actorId="<layerId>", items=[
{ action:"create", data:{ id:"<actorId>", type:"node", position:{x:0,y:0},
layerSettings:{ isTextNode:true, textNodeScale:1.5, textWidth:320, textHeight:44 } } }
])
isTextNode:true — render the node as a text labeltextNodeScale — font-size multipliertextWidth / textHeight — text-box size in px. Scale it to the text — roughly
16·scale px per character wide and 28·scale px per line tall — or large/long text wraps
and breaks mid-word.Good for section titles, axis labels and annotations on a graph. To change it, delete the
placement and re-create it with the new layerSettings.
layerId: "<uuid>" # layer actor UUID
actors:
- id: local_name # local id for new actors; UUID for existing ones
title: "My Block"
formName: "Process" # use formName from the catalog (preferred over formId)
color: "#539fdf" # hex color string — always set for flowchart blocks
description: "..." # optional
picture: "" # optional
position:
x: 0 # horizontal position on layer
y: 130 # vertical position on layer
edges:
- source: local_name_or_uuid # references actor id field
target: local_name_or_uuid
source_title: "My Block" # informational only, not sent to server
target_title: "Other Block"
Rules:
id values are local references used only within the file for edge wiring.
After pushGraphFile all local ids are replaced with server UUIDs.data for system forms — the server auto-injects shape/view.formName takes priority over formId when both are present.edges reference actor id fields (local names work, they are resolved at push time).To put any image on a graph — a line, a circle, a rectangle, an icon, a logo, a
hand-drawn shape, a small diagram, anything a PNG/SVG can hold — create an actor with a
pictureObject: a custom image rendered AS the node body (the backend's "napkin"
element), instead of a standard form node. A divider line is just one use.
createActor(formId=3279, color="#F04438", pictureObject={
img: "data:image/png;base64,iVBORw0KGgo…", // a PNG/SVG data URI
width: 800, // display size on the canvas
height: 8,
type: "napkin"
}, contextLayerId="<layerId>")
img — the image as a data URI (e.g. a thin red PNG to draw a divider line).width / height — display size in px. The image is anchored at its centre and
keeps the source's aspect ratio (set width; height follows), so for a thin line
make the source PNG wide-and-short.type: "napkin" — the custom-image element kind.A classic use is an ADAM/EVE-style horizontal divider: a wide, short red dashed PNG
placed across the middle of the layer. Change a node's image later with the same
pictureObject on updateActor.
Never hardcode coordinates. Calculate using dagre/Sugiyama layout.
SIZES = {
"Start / Stop": { w: 200, h: 50 },
"Process": { w: 200, h: 50 },
"Predefined Process": { w: 200, h: 100 },
"Decision": { w: 200, h: 100 },
"Data": { w: 200, h: 60 },
"Document": { w: 200, h: 70 }
}
default: { w: 200, h: 50 }
rank[start] = 0
for each edge source → target:
rank[target] = max(rank[target] ?? 0, rank[source] + 1)
nodeSep(rank) = max(max_w_in_rank * 0.3, 60) // horizontal gap between centers
rankSep(r) = max(max_h_in_rank(r) * 1.2, 80) // vertical gap between rows
y[rank_0] = 0
y[rank_n] = y[rank_n-1] + max_h(rank_n-1)/2 + rankSep(rank_n-1) + max_h(rank_n)/2
for each rank with N nodes:
center_to_center = max_w_in_rank + nodeSep
total_span = (N - 1) * center_to_center
x[node_i] = -total_span / 2 + i * center_to_center
// Step 1 — Create Graph + Layer (skip if layerId already known)
createActor(formName="Graphs", title="Order Processing") → graphId
createActor(formName="Layers", title="Order Processing") → layerId
createLink(source="<graphId>", target="<layerId>")
// Step 2 — Write <layerId>.yaml
layerId: "<layerId>"
actors:
- id: start
title: "Start"
formName: "Start / Stop"
color: "#17B26A"
position: { x: 0, y: 0 }
- id: validate
title: "Validate Order"
formName: "Process"
color: "#539fdf"
position: { x: 0, y: 130 }
- id: check
title: "Valid?"
formName: "Decision"
color: "#F79009"
position: { x: 0, y: 285 }
- id: process
title: "Process Payment"
formName: "Process"
color: "#539fdf"
position: { x: 260, y: 440 }
- id: error
title: "Return Error"
formName: "Process"
color: "#F04438"
position: { x: -260, y: 440 }
- id: end
title: "End"
formName: "Start / Stop"
color: "#17B26A"
position: { x: 0, y: 595 }
edges:
- source: start
target: validate
- source: validate
target: check
- source: check
target: process
- source: check
target: error
- source: process
target: end
- source: error
target: end
// Step 3 — Push to server
pushGraphFile(layerId="<layerId>")
// → all actors created, edges drawn, file updated with server UUIDs
Pass formName to createActor or in the graph YAML file.
The server resolves the name to ID automatically.
| formName | Usage |
|---|---|
"Graphs" | Graph container actor |
"Layers" | Layer (visual canvas) actor |
Always set
color(hex string) for flowchart blocks.
| formName | Description |
|---|---|
"Start / Stop" | Start or end node |
"Process" | Process step |
"Decision" | Decision diamond |
"Predefined Process" | Predefined / subroutine process |
"Document" | Document shape |
"Data" | Data parallelogram |
"Documents" | Multi-document stack |
"Stored Data" | Stored data shape |
"Off-page Reference" | Off-page connector |
"Preparation" | Preparation / initialization |
"API Call" | API call block |
"Manual Input" | Manual input |
"Delay" | Delay block |
"Database" | Database cylinder |
"Manual Operation" | Manual operation |
"Terminator" | Oval terminator |
"Root Node" | Root node |
"Null" | Universal generic node |
"Flowchart" | Nested flowchart reference |
| formName | Description |
|---|---|
"Petri Net" | Petri net diagram |
"Sequence Diagram" | Sequence diagram |
"Actor Graph" | Actor graph |
"Mind Map" | Mind map |
"Corezoid" | Corezoid process diagram |
| formName | Description |
|---|---|
"Corezoid Start" | Entry point |
"Corezoid API Call" | External API call |
"Corezoid Condition" | Condition / routing |
"Corezoid Code" | Code execution |
"Corezoid Copy Task" | Copy task |
"Corezoid Modify Task" | Modify task |
"Corezoid Sum" | Aggregation / sum |
"Corezoid Delay" | Time delay |
"Corezoid Database Call" | DB call |
"Corezoid Waiting for Callback" | Async wait |
"Corezoid Call a Process" | Sub-process call |
"Corezoid Reply to Process" | Reply to caller |
"Corezoid Queue" | Queue node |
"Corezoid Get from Queue" | Dequeue |
"Corezoid Set Parameters" | Set task parameters |
"Corezoid End: Success" | Success terminal |
"Corezoid End: Error" | Error terminal |
"Corezoid GIT Call" | GIT call |
| formName | formName | formName |
|---|---|---|
"EC2" | "Lambda" | "RDS" |
"App Runner" | "EKS Cloud" | "EKS Distro" |
"EFS" | "Client VPN" | "Elastic Container Registry" |
"Certificate Manager" | "Simple Queue Service" | "Inspector" |
"GuardDuty" | "Data Pipeline" | "Kinesis" |
"Key Management Service" | "DynamoDB" | "Elastic Kubernetes Service" |
"Secrets Manager" | "Elastic Load Balancing" | "Route 53" |
"ElastiCache" | "CloudWatch" | "Transfer Family" |
"Managed Streaming for Apache Kafka" | "Amplify" | "Budgets" |
"Simple Storage Service" | "Transit Gateway" | "Network Firewall" |
"OpenSearch Service" | "WAF" | "Virtual Private Cloud" |
"Simple Notification Service" | "API Gateway" | "Transcribe" |
"Athena" | "QuickSight" | "CloudFront" |
"Global Accelerator" | "Backup" | "DocumentDB" |
"Glue" | "RDS on VMware" |
| formName | Description |
|---|---|
"GPT" | GPT model node |
"Anthropic" | Anthropic model node |
"Grok" | Grok model node |
"SLM" | Small language model |
"Devin" | Devin AI agent |
"Black box (Mind)" | Opaque mind component |
"Black box (LLM)" | Opaque LLM component |
"Black box (Actor)" | Opaque actor component |
"Black box (Seq)" | Opaque sequence component |
"Grey box (Actor)" | Semi-transparent actor |
"Grey box (Mind)" | Semi-transparent mind |
"Grey box (LLM)" | Semi-transparent LLM |
"Grey box (Seq)" | Semi-transparent sequence |
| formName | Description |
|---|---|
"Class" | UML class |
"Interface" | UML interface |
"Component" | UML component |
"Package" | UML package |
"Artifact" | UML artifact |
| formName | Description |
|---|---|
"Human" | Human participant |
"Node" | Generic network node |
"Branch Node" | Branch point |
"White box" | Transparent white container |
"Place" | Physical location |
"Token" | Token / badge |
"Flag" | Flag marker |
"Program" | Program block |
"Current Position" | Current position marker |
"Transition" | State transition |
"Stubnet" | Stub network |
"Marketplace" | Marketplace node |
Use these for targeted queries, not for building graphs (use the file workflow instead).
Call tools with named arguments (the curated v2 tools take typed params — there is no
single body blob). A layer is addressed by its layer-actor UUID — the actorId
argument of the layer tools below (the same id the file workflow calls the layerId).
createActor(formName="Process", title="Step", color="#539fdf") // one actor per call — there is no createActors
getActor(actorId="<actorId>")
getActorByRef(formId=<formId>, ref="<ref>")
updateActor(formId=<formId>, actorId="<actorId>", title="Updated")
deleteActor(actorId="<actorId>") // one at a time — there is no bulk-actor delete
// Single link. edgeTypeId defaults to the workspace hierarchy type when omitted —
// pass it only for a different edge type (see getEdgeTypes).
createLink(source="<a>", target="<b>") → edgeId
// Up to 50 links in one call; each object's edgeTypeId is optional (same hierarchy default).
massLink(links=[{"source":"<a>","target":"<b>"}, ...])
// massLink/createLink create the logical edge only. To also show an edge on a layer,
// place it with manageLayerActors (actorId = the layer-actor UUID):
manageLayerActors(actorId="<layerActorId>", items=[{"action":"create","data":{"id":"<edgeId>","type":"edge","laIdSource":<laA>,"laIdTarget":<laB>}}])
// Edge LINE STYLE — put it in the placed edge's data.layerSettings.lineStyle
// (solid | dashed | dotted; omit → solid). Use distinct styles for distinct
// edge meanings (e.g. dashed = dependency, dotted = hint, solid = hierarchy):
manageLayerActors(actorId="<layerActorId>", items=[{"action":"create","data":{"id":"<edgeId>","type":"edge","laIdSource":<laA>,"laIdTarget":<laB>,"layerSettings":{"lineStyle":"dashed"}}}])
// To CHANGE an edge's style, DELETE its placement then create it again — re-creating
// without deleting first adds a DUPLICATE placement (the line is drawn twice).
// existLink REQUIRES edgeTypeId (unlike createLink) — pass it explicitly to find/dedupe an edge by its endpoints:
existLink(source="<a>", target="<b>", edgeTypeId=<id>) → edge id if it exists
getEdge(edgeId="<edgeId>")
updateEdge(edgeId="<edgeId>", name="New label", pinned=true) // fields: name, linkedActorId, curveStyle, pinned
deleteEdge(edgeId="<edgeId>")
deleteEdgesByNodes(links=[{"source":"<a>","target":"<b>","edgeTypeId":<id>}]) // bulk delete by endpoints (1-200); edgeTypeId required per object
Reading "the nodes/actors on a graph" means reading THAT LAYER's placements — never a
workspace-wide search. searchActors / filterActors without a layer constraint scan the
whole workspace and return unrelated actors (chats, daily reports, other forms) — they are the
WRONG tool for "what's on this graph/layer". Use the layer-read tools below, addressing the
layer by its layer-actor UUID.
Getting the layer UUID from a pasted URL. Simulator graph URLs look like:
https://<host>/actors_graph/<workspaceId>/graph/<graphActorId>/layers/<layerActorId>
└ workspace ┘ └ graph root ┘ └ THE LAYER ┘
actorId is the last UUID — the segment after /layers/. Read it with the
tools below (e.g. getLayerActorsPaginated(actorId="<layerActorId>"))./graph/<id>/<mode> (mode = layers|actors|trees) with no
/layers/ segment, that <id> is the layer/graph-folder to open./graph/ is the graph ROOT actor (a Graphs-form actor), not the canvas of
nodes — don't read it expecting the layer's members.// READING A LAYER — default to the paginated read; it works for a layer of ANY size.
// Start with layerStats to learn the counts, then page nodes and edges:
layerStats(actorId="<layerActorId>") // node/edge counts — call first
getLayerActorsPaginated(actorId="<layerActorId>", type="nodes", limit=50, offset=0) // walk offset until a short/empty page
getLayerActorsPaginated(actorId="<layerActorId>", type="edges", limit=50, offset=0) // then the edges
getAllLayerPlacements(layerId="<layerActorId>") // nodes-only one-shot shortcut (engine tool, any size)
searchLayerActors(actorId="<layerActorId>", query="...")
// getLayerActors is a SMALL-LAYER shortcut only — it loads the whole layer (nodes + edges)
// at once and the backend REJECTS it with a 400 ("Layer is too large (… nodes, … edges,
// total: …). Maximum allowed: N.") once nodes+edges exceed the size cap (~300). Do not reach
// for it first to read a layer; if you do and hit that error, switch to getLayerActorsPaginated
// (do NOT give up). `filter` keeps each page small.
getLayerActors(actorId="<layerActorId>") // whole layer in one call — small layers only
existLayerElement(actorId="<layerActorId>", id="<actorOrEdgeId>", type="actor") // is a node/edge on the layer — dedupe before placing
moveActors(sourceActorId="<layerA>", targetActorId="<layerB>", items=[{"actorId":"<a1>"}]) // ≤10 actors between layers
cleanGraphLayer(actorId="<layerActorId>") // wipe the layer (actors remain) — destructive
getRelatedActors(type="linked", actorId="<actorId>") // type: "linked"|"parents"|"children"; defaults to the hierarchy link type
getActorLinks(actorId="<actorId>") // every edge of an actor
getLinkedActors(actorId="<actorId>") // directly-linked actors across edge types
// Related actors filtered/ranked by an account balance, in one query:
// "the actors related to X whose account N balance is > / < a value".
filterActors(formId=<formId>, linkedToActorId="<anchorActorId>",
accountNameId="<nameId>", currencyId=<id>,
amountFrom=<min>, amountTo=<max>, // amountFrom = balance >=, amountTo = balance <=
orderBy="balance", orderValue="DESC") // omit linkedToActorId for a form-wide ranking
// linkedToActorDirection="children" keeps only the anchor's children (expand/collapse a node),
// "parents" only its parents; omit / "both" = either direction (default).
// Save tokens: every read/traversal tool above (getRelatedActors, getActor,
// getLayerActors, searchLayerActors, searchActors, filterActors, ...) accepts an
// optional `filter` field-selection arg — comma-separated fields to return, e.g.
// filter="id,title,formId" (dotted paths like data.status pick nested data fields).
// The server returns only those fields. NOT a row filter (that's search / q).
getRelatedActors(type="children", actorId="<actorId>", filter="id,title,formId")
Accounts are a finance concern — see
/simulator-financefor the full workflow. Quick reference:
getAccounts(actorId="<actor>")
getAccount(accountId="<acc>")
createAccountPair(accountName="Balance", currencyName="USD") // bootstrap: creates name + currency if missing AND grants pair access
createAccount(actorId="<actor>", nameId="<name>", currencyId="<cur>", accountType="default")
setAccountAmount(accountId="<acc>", amount=1000) // fixed-balance correction
deleteAccount(actorId="<actor>", currencyId="<cur>", nameId="<name>", accountType="default")
getCurrencies()
createCurrency(name="USD", symbol="$", precision=2)
getAccountNames()
createAccountName(name="Balance", abbreviation="BAL")
pushGraphFile.formName takes priority over formId in the YAML file and in createActor calls.data in the YAML for system forms — the server auto-injects shape/view/blockId.getForm(formId) first, then build actors.data from the returned field schema using field name as keys.color (hex string) for flowchart blocks.id values in the YAML are replaced with server UUIDs after pushGraphFile. Use short readable names (
start, validate, end) for new actors.pullGraphFile → edit → pushGraphFile is the standard edit cycle for existing layers.laId ≠ actorId. The file workflow handles laId management automatically.getSystemActor(objType="user", objId=<userId>) and place that actorId. A bare userId
is not a graph node. (See $CLAUDE_PLUGIN_ROOT/docs/entities/users.md.)| Path | When to read |
|---|---|
$CLAUDE_PLUGIN_ROOT/docs/entities/actors.md | Full actor property list and types |
$CLAUDE_PLUGIN_ROOT/docs/entities/links.md | Link/edge properties and type system |
$CLAUDE_PLUGIN_ROOT/docs/entities/layers.md | Layer types and behavior |
$CLAUDE_PLUGIN_ROOT/docs/user-flows/graph-functionality.md | Graph building walkthrough with test scenarios |
$CLAUDE_PLUGIN_ROOT/docs/user-flows/actor-graph-management.md | Managing actors on graphs — practical patterns |
npx claudepluginhub corezoid/simulator-ai-plugin --plugin simulatorRenders interactive step-by-step diagrams of systems, workflows, architectures, and user journeys. Replaces prose or static diagrams with clickable data flows.
Generates draw.io (.drawio) XML files for logical flow diagrams, abstract system architectures, BPMN processes, UML diagrams, DFDs, decision flowcharts, and system interactions.
Generates and edits diagrams in draw.io using built-in AI with Gemini, Claude, or OpenAI from natural language prompts. Covers API configuration, custom LLM endpoints, and mxGraph XML best practices.