Skip to main content
Glama

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault
FLEXSIM_LOG_LEVELNoOverride logging level
FLEXSIM_CONFIG_PATHNoPath to an alternate config.toml
FLEXSIM_INSTALL_PATHNoOverride FlexSim program path
FLEXSIM_PYTHON_VERSIONNoOverride Python version for FlexSimPy3.12

Instructions

Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.

This server publishes no instructions, or was last inspected before Glama recorded them.

Capabilities

Features and capabilities supported by this server

Protocol revision2025-11-25

CapabilityDetails
tools
{
  "listChanged": false
}
prompts
{
  "listChanged": false
}
resources
{
  "subscribe": false,
  "listChanged": false
}
experimental
{}

Tools

Functions exposed to the LLM to take actions

NameDescription
flexsim_open_modelB

Open a FlexSim model file.

Args:
    model_path: Path to .fsm or .fsx file

Example:
    model_path="C:/Models/warehouse.fsm"
flexsim_gui_statusA

Say whether a FlexSim window (Student license, can create objects) is connected.

To connect: flexsim_prepare_gui_model opens a copy of the template in FlexSim, which connects
by itself (On Model Open runs mcp_start()). While connected, the script/node/run tools act on
that window instead of the Python backend.
flexsim_prepare_gui_modelA

Copy the GUI template to a new .fsm and open that copy in the FlexSim window.

Work on the copy, never on the template. The model's On Model Open trigger runs mcp_start(),
so the window connects by itself in about 20 s; check flexsim_gui_status. If it does not
(for example the MCP server was restarted), run `mcp_start();` in its Script console.

Args:
    model_path: Path of the new .fsm file (must not exist yet)
flexsim_resetB

Reset simulation to initial state (time = 0).

flexsim_runB

Start running the simulation continuously (returns immediately).

Args:
    run_speed: Optional run speed multiplier (> 0)
flexsim_run_to_timeA

Run simulation until reaching target time (blocks until done, max speed).

Args:
    target_time: Target simulation time in seconds

Example:
    target_time=3600  # Run for 1 hour

Note: time only advances while the model has scheduled events.
With the FlexSim window connected, run_speed (optional) sets the Run Speed slider first;
use 1000 for a fast run (the window default is 4x real time).
flexsim_stopA

Stop the running simulation.

flexsim_get_timeB

Get current simulation time.

flexsim_get_parameterA

Read a model parameter defined in the model's Parameter Tables.

Dual backend: with the FlexSim window connected reads
Model.parameters.<name> by script; without window uses
controller.getParameter. A missing window parameter is an error
(create it with flexsim_ensure_parameter, this tool never creates).

Args:
    name: Parameter name as defined in the model
flexsim_set_parameterA

Set a model parameter defined in the model's Parameter Tables.

Dual backend: with the FlexSim window connected writes
Model.parameters.<name> by script; without window uses
controller.setParameter. A missing window parameter is an error
(create it with flexsim_ensure_parameter, this tool never creates).

Args:
    name: Parameter name as defined in the model
    value: New value (number or string)
flexsim_get_performance_measureA

Read a performance measure defined in the model (Statistics > Performance Measures).

This is the native way to get statistics out of a model, e.g. utilization or throughput.

Dual backend: with the FlexSim window connected reads the treenode
Model.find("Tools/PerformanceMeasureTables/PerformanceMeasures>variables/measures/<name>")
by script (discovery inline: when that path does not resolve, lists
Tools/PerformanceMeasureTables and returns the available measures as help);
without window uses controller.getPerformanceMeasure. Never creates nor
modifies rows (PerformanceMeasureTables.addRow broke OnReset).

Args:
    name: Performance measure name as defined in the model
flexsim_evaluate_nodeA

Evaluate an EXISTING node by tree path and return its value.

This does not run arbitrary FlexScript (use flexsim_execute_script for that). An
unknown path returns None without an error.

Args:
    node_path: e.g. "MAIN:/project/exec/time"
    args: Optional parameters passed to the node's code

Example:
    node_path="MAIN:/project/exec/time"
flexsim_bridge_statusA

Check whether the FlexScript bridge node is configured and answering.

flexsim_execute_scriptA

Execute FlexScript inside the open model and return its result.

Needs the bridge node (see flexsim_bridge_status). End the script with `return <value>;`
to get a value back.

Safety: ArrivalStyle/FlowItemClass writes, PerformanceMeasureTables.addRow and
overwriting bridge/mcp_template.fsm are blocked (they hung or corrupted FlexSim).

Args:
    script: FlexScript code

Examples:
    script='return Model.find("Queue1").subnodes.length;'
    script='return getmodeltime();'
flexsim_read_consoleA

Read FlexSim's consoles (window backend): compile errors, runtime exceptions and prints.

Args:
    console: compiler, system, output or script (the editor text). Default compiler.
    last_chars: how many trailing characters to return (default 3000).
    clear: not supported by FlexSim from here; use flexsim_execute_script(`clearconsole();`) for the
    output console.
flexsim_screenshotA

Take a picture of the FlexSim window so you can SEE the model (window backend).

Args:
    region: window (everything: 3D view, panels, consoles) or view3d (only the 3D view, bigger).
    scale: 0.2-1.0 size factor (default 0.6; 1.0 for detail).
    format: jpeg (small, default) or png.
    method: auto (default), printwindow (works while covered by other windows) or screen.

The 3D view crop (view3d) assumes the default layout of the template.
With a rearranged UI (panels moved/resized) the fixed VIEW3D_CROP fractions
(utility/capture.py) may miss the 3D view: use region="window" then, and see
flexsim_set_view for framing hints.
flexsim_inspect_modelA

List every object of the model in the FlexSim window as JSON (window backend).

For each object: name, class, position [x, y, z], `to` (flow connections), `resources` (resource
connections such as an Operator) and, for Source/Queue/Processor/Sink/Combiner/Separator/MultiProcessor,
`stats` {input, output, content_avg, content_max}. Also returns the simulation time.

Student (100 objetos, docs oficiales FlexSim): si count>=85 agrega `license_hint` # CNT-limite
"85+/100 objetos (límite licencia Student; ver GUIA)"; si count>100 informa igual # CNT-limite
(la ventana lo permite abrir pero FlexSim bloquea). # CNT-limite
flexsim_get_node_valueC

Get value from a FlexSim tree node (needs the bridge).

Args:
    node_path: Path to node (e.g., "Model/Queue1/stats/input")
flexsim_set_node_valueB

Set value in a FlexSim tree node (needs the bridge).

Args:
    node_path: Path to node
    value: New value to set

Example:
    node_path="Model/Processor1/variables/processtime"
    value=5.0
flexsim_stepC

Step through simulation events (needs the bridge).

Args:
    steps: Number of events to step (1-1000, default: 1)
flexsim_new_modelA

Create a new model from the bridge template and open it.

FlexSim has no script command to create a blank model, so this copies the template
(config.toml [bridge] template_model, which already contains the bridge node) to a new
.fsm file and opens it.

Args:
    model_path: Path of the new .fsm file (must not exist yet)

Example:
    model_path="C:/Models/warehouse.fsm"
flexsim_save_modelA

Save the current model (needs the bridge).

Saves to the file the model was opened from; with save_path, also copies it there.

Args:
    save_path: Optional extra destination for a copy of the saved model

Example:
    save_path="C:/Models/warehouse_v2.fsm"
flexsim_create_objectsA

Create objects in the FlexSim window in batch (window backend only).

Wraps Object.create + name + setLocation + setProperty with per-object verification.
Names must be unique (FlexSim allows duplicates, this tool refuses them unless
overwrite=True, in which case the old object is destroyed first).

Student (100 objetos, docs oficiales FlexSim): antes de crear lee # CNT-limite
_model_object_count(); si actual+len(batch)>100 devuelve error SIN crear nada # CNT-limite
(atómico); si actual+len>=85 procede pero antepone warning al resultado. Solo ventana. # CNT-limite

Args:
    objects: [{cls, name, x, y, z, properties}]. Class e.g. Source, Queue,
        Processor, Sink, Operator, Rack, Conveyor::StraightConveyor.
    overwrite: destroy Model.find(name) first when it already exists.

Example:
    objects=[{cls: Source, name: Source1, x: 0}, {cls: Queue, name: Queue1, x: 6}]
flexsim_connect_objectsA

Connect two objects (window backend only).

flow: contextdragconnection(source, target, "A") — item flow (non-deprecated form).
resource: contextdragconnection(requester, resource, "S") — e.g. Processor asks Operator.
NOTE: connects ACCUMULATE (each call adds a parallel link: 1-2-3...). Disconnect
(Q/W via flexsim_disconnect_objects) before reconnecting to avoid duplicates.

Args:
    source: origin (or requester for resource).
    target: destination (or resource for resource).
    kind: flow or resource.
    verify: check outObjects/inObjects (flow) or centerObjects (resource).
flexsim_layout_lineA

Place existing objects along X or Y (window backend only).

New objects spawn at the origin; this lines them up like Source(0) Queue(6) ...
Default spacing is 6 m (recipe in AGENTE_FLEXSIM.md sec 9).

Args:
    names: existing objects in line order.
    start_x, y, z: position of the first object.
    spacing: gap in meters (default 6).
    axis: x or y.
flexsim_get_statisticsA

Read TrackedVariable statistics as JSON (window or Python backend).

Replaces hand-written `k.stats.input.value` reads. Metrics are
<group>.<field> with group in input/output/content/staytime and field in
value/average/maximum/minimum/count/rate.

Args:
    objects: names to read (default: all model objects).
    metrics: e.g. ["input.value", "content.average", "staytime.average"].
    include_time: include Model.time (default True).
flexsim_diagnoseA

Read-only environment diagnosis (never runs write scripts).

Checks: port 8765 free/used, window connected + last poll age, bridge ping,
FlexSimPy importable, template exists. Reuses utility/doctor.py logic.
flexsim_run_experimentsA

Run a parameter sweep with replications and write summary.csv + manifest.json (Batch MVP).

For each scenario x seed: reset -> apply Model.parameters -> randinit(Source, seed)
-> run_to_time -> get_statistics. Seeds only diverge in the window backend
(Python ignores randinit). Never touches PerformanceMeasureTables.

Args:
    scenarios: [{name, params: {Rate: 5}}] (params go to Model.parameters.<name>).
    seeds: e.g. [101, 202, 303].
    run_to_time: simulated seconds.
    run_speed: Run Speed slider (window only, default 1000).
    statistics: {objects, metrics} (default content/input/output basics).
    artifact_dir: new directory for summary.csv + manifest.json.
    stop_on_error: abort on first failing replica.
flexsim_clone_modelA

Clone the current model file or clear back to the GUI template.

clone: save the window (when connected), copy _gui_model_path or _current_model
    (source_path only when no known origin) to dest_path.
clear: copy the GUI template to dest_path (like flexsim_prepare_gui_model).
With open_in_gui, the copy is opened in FlexSim like flexsim_prepare_gui_model.

Args:
    dest_path: new .fsm file (must not exist, never the template).
    source_path: optional existing .fsm used only when no known origin.
    mode: clone or clear (default clone).
    open_in_gui: open the copy in FlexSim (default True).
flexsim_export_resultsB

Export a statistics snapshot to artifact_dir as CSV or JSON.

Reuses _collect_statistics (never touches PerformanceMeasures).

Args:
    artifact_dir: directory for the snapshot (created when missing).
    format: csv or json (default csv).
    filename: snapshot file name (default snapshot.csv).
    statistics: {objects, metrics} (default content/input/output basics).
flexsim_attach_resourceA

Attach a resource to a requester (window backend only).

Runs contextdragconnection(requester, resource, "S") and checks the last
centerObjects entry. With mode=shared the same resource feeds N requesters
(exclusivity is not enforced, only reported).

Args:
    requester: object asking for the resource (e.g. Processor1).
    resource: resource object (e.g. Operator1).
    mode: exclusive or shared (default exclusive).
    verify: check centerObjects (default True).
flexsim_table_writeA

Write a rectangular block of values into a GlobalTable, creating it when needed.

Writes rows (list of lists, 1-100 rows x 1-20 cols, rectangular) into
Table("<table>") cells [1..R][1..C] with quoted literals. With clear=True
(default) the table is resized to exactly R x C first; with clear=False it is
only enlarged and the block lands at the top-left. Returns JSON
{table, rows, cols, table_rows, table_cols, created, cleared, backend}.

Creation (create_if_missing=True, default) tries, in order: Tools.create("GlobalTable")
(Toolbox factory: the only path verified end-to-end — create, setSize, cell R/W),
applicationcommand("addglobaltable"), and cloning the first existing table. It works
even when Tools/GlobalTables is empty or missing. Object.create("GlobalTable") is
never used: its nodes fail with `Invalid row number` (AGENTE_FLEXSIM.md sec 3c).

Args:
    table: GlobalTable name (Tools/GlobalTables/<table>).
    rows: rectangular values (numbers/strings/bool/null; null stored as 0).
    create_if_missing: clone a donor table when missing (default True).
    clear: resize to exactly the block size first (default True).
flexsim_table_readA

Read a GlobalTable as JSON {table, rows, cols, total_rows, total_cols, truncated, data}.

Reads Table("<table>")[r][c] in row batches (braces work inside executestring,
AGENTE_FLEXSIM.md sec 3c) and joins cells with "|" and rows with ";" (the same join
style flexsim_inspect_model already relies on). Numeric cells come back as numbers,
the rest as strings.

Limits (documented, no silent magic): string cells containing "|" or ";" split
ambiguously (a "warning" field is added when the split does not match the table
dims); bundle/object cells read as their text form. Tables larger than max_rows x
max_cols return the top-left window with truncated=True.

Args:
    table: GlobalTable name (Tools/GlobalTables/<table>).
    max_rows: cap 1-100 (default 100).
    max_cols: cap 1-20 (default 20).
flexsim_set_viewA

Frame the 3D view on an object, safely (no invented camera commands).

Safe subset, because this repo has no verified FlexScript camera/fit API: verifies
the target exists via Model.find, reports its position (or points at
flexsim_inspect_model + flexsim_layout_line for target="all"), runs the proven
repaintall() and tells you to check with flexsim_screenshot. The requested view
(3d/top/front) is recorded as intent; nothing else touches the camera, so this tool
can neither hang nor move the model. To frame by hand use the view cube / View menu,
then screenshot.

Limit: flexsim_screenshot region="view3d" crops fixed VIEW3D_CROP fractions
(utility/capture.py:131) tuned for the default template layout; with a rearranged UI
prefer region="window". flexsim_screenshot itself is left intact (no custom crop
parameter: parsing arbitrary crops adds risk for zero verified benefit).

Args:
    target: object name or "all" (default).
    view: 3d (default), top or front (recorded intent, see above).
flexsim_set_distributionA

Set a time property distribution by read-modify-rewrite (window backend only).

Verified: exponential par2 (media) via ", <viejo>/**/" mold replace + constant
via setProperty(number) (AGENTE_FLEXSIM.md sec 6). uniform/normal rewrite the
two tagged numbers ONLY when the current code already is that distribution.
CHANGING FAMILY (exponential->uniform/normal) IS REFUSED: the 2026 mold has
par1..par5 slots and a renamed mold leaves par4/par5 empty
(uniform(2,8,stream,,)), which CRASHED FlexSim live (2026-09-26 probe6:
process gone during the next run). Change family by hand in the GUI or use
constant. The stream argument is accepted but the original stream expression
is preserved (third param untouched).

The uniform/normal/exponential (y constant) output reports expected_mean
(exponential=par2, uniform=(par1+par2)/2, normal=par1, constant=valor)
para contraste humano.

Functional check (humano): flexsim_reset + flexsim_run_to_time hasta T +
leer Source.stats.output (arrivals) y contrastar arrivals ~= T / expected_mean
(p. ej. T=600, mean=5 -> ~120 llegadas; tolerancia por variabilidad aleatoria).

Args:
    object: object name (e.g. Source1).
    property: property name (default InterArrivalTime).
    distribution: exponential, uniform, normal or constant.
    par1: uniform min / normal mean / constant value (when par1 set).
    par2: exponential mean / uniform max / normal std / constant value (fallback).
    stream: accepted for API completeness, preserved as-is (not rewritten).

Limits: only the standard mold with /**/ tags is rewritten; a mold
    without tags returns an error (no fallback to constant, no corruption).
    Family changes (exponential->uniform/normal) are refused: they crashed
    FlexSim live (empty par4/par5 slots). Change family in the GUI or use
    constant.
flexsim_add_conveyor_legA

Add one Conveyor::StraightConveyor leg (window backend only).

Creates Conveyor::StraightConveyor, setLocation, tries o.size.x = length
and reads it back. NOTE (verificado con screenshots 2026-09-27): o.size
se GUARDA pero la geometría visual del StraightConveyor queda en ~10 m
(Conveyor.length es readonly); verificar siempre con screenshot y dejar
hueco de ~12 m en el layout. With from_object/to_object, builds the
transfer chain (verificado en vivo): contextdragconnection "A" (crea
Entry/ExitTransfer) + borrado selectivo de directos por puerto +
objectconnect de los stubs. Solo objectconnect deja belt.in en 0.

AUTO-LAYOUT (respuesta a "¿a ojo?"): si x/y/length se omiten (None) y
from_object+to_object están dados, la posición sale de los extremos
(punto medio) y length = dist-1 (mín 2). El flujo (puertos) es 100%
lógico y verificado; lo visual se calcula, no se estima.

Verification walks the outObjects chain (BFS, up to 4 hops) because conveyors
interpose EntryTransfer*/ExitTransfer* objects: a working leg reports e.g.
SrcT>EntryTransfer1>BeltT.

Args:
    name: base name (default Belt; auto Belt1/Belt2... when taken).
    length: metres (default 10; None = auto dist-1). Se guarda en o.size
        pero el visual puede quedar en 10 m: verificar con screenshot.
    x, y, z: position of the new belt (None = punto medio, z = promedio).
    from_object: optional flow origin (from_object -> belt).
    to_object: optional flow destination (belt -> to_object).

Limits: visual length fixed ~10 m (readonly); cuando FlexSim ignora el
    write se reporta igual (leído de vuelta).
flexsim_processflow_pingA

Inventory a ProcessFlow without building activities (window backend only).

Creates ProcessFlow::ProcessFlow when missing (create_if_missing) and lists
existing subnodes/activities via Model.find + subnodes loop. Never builds
activities (no verificado, AGENTE_FLEXSIM.md sec 3c). See also
flexsim_processflow_inventory for read-only activity detail
(class + in/out counts, never creates).

Args:
    name: ProcessFlow object name (default ProcessFlow1).
    create_if_missing: create it when absent (default True).

Returns JSON {exists, activities, note} (plus name for traceability).
flexsim_disconnect_objectsA

Disconnect two objects (window backend only).

Uses contextdragconnection with the DISCONNECT keys from the official command
reference (FlexSim 2026 help/CommandReference/Commands.xml, defaultdragconnection
entry): 'Q' disconnects output->input ports (flow), 'W' disconnects central
ports (resource). There is NO objectdisconnect command in FlexSim 2026 (verified
in Commands.xml and live: it returns 0 and changes nothing).
NOTE: connects ACCUMULATE (each connect adds a parallel link), so disconnect
all matches before reconnecting.
flow verifica que target ya no esta en outObjects de source; resource verifica
que target ya no esta en centerObjects de source. Devuelve OK/FAIL con estado post.

Args:
    source: origin (or requester for resource).
    target: destination (or resource for resource).
    kind: flow or resource.
    verify: check outObjects (flow) or centerObjects (resource).
flexsim_save_model_asA

Save the window model under a NEW path and CONTINUE THERE (window backend only).

True Save-As (a filesystem copy alone would leave FlexSim showing the old file,
diverging bookkeeping from reality): saves the open window, copies the file to
dest_path, OPENS the copy in a new FlexSim window, waits for its title to appear,
then closes the old window (by its exact previous title) and repoints
_gui_model_path/_gui_model_name/_current_model to dest. The new window connects
by itself in ~20 s (On Model Open runs mcp_start()); check flexsim_gui_status.
If the new window never appears, nothing is closed or repointed (safe).
Unlike flexsim_save_model (snapshot copy, state stays), dest becomes active.
Bloquea dest == plantilla (GUI o bridge).

Args:
    dest_path: new .fsm file (must not exist, never the template).
flexsim_close_modelA

Close the window model, optionally saving first (window backend only).

Metodo: NO hay comando de cierre verificado en el repo (grep applicationcommand
solo muestra save/run/stop/reset/openmodel/addglobaltable; no existe "close" ni
fileclose verificable, asi que no se inventa ninguno). Con save=True se ejecuta
applicationcommand("save") primero; el cierre mata SOLO la ventana del modelo
actual filtrando por titulo _gui_model_name via powershell Stop-Process con
filtro MainWindowTitle (mismo patron que tests/integration/test_gui_backend.py),
y limpia _gui_model_path. Devuelve OK.

Args:
    save: run applicationcommand("save") before closing (default False).
flexsim_list_treeC

List tree subnodes as JSON (window or Python backend via run_script).

Usa run_script (dual, como flexsim_get_node_value), NO evaluate_node (solo-Python),
asi funciona en ventana. Dado path lista subnodos directos (nombre + clase via
node() + subnodes loop + getname(classobject())) hasta depth con limite max_nodes;
para depth>1 itera niveles con un script por nodo/padre desde Python. Devuelve
JSON {path, nodes: [{name, class, path}]}. path se valida contra inyeccion (solo
alfanumericos, /, _, -, >, :, espacios; rechaza ".." y ";").

Args:
    path: tree path (default "Model").
    depth: 0-3 levels (default 1; 0 returns no children).
    max_nodes: cap for returned nodes (default 100).
flexsim_add_agv_loopA

Create a pilot AGV loop: N ControlPoints in rectangle + one StraightPath (window backend only).

Crea n_points AGV::ControlPoint (<prefix>CP1..N) en rectangulo de lado spacing
desde (x, y, z) + UN AGV::StraightPath piloto del punto 1 al 2. Cada creacion
se verifica con return o.name. La conexion fisica entre paths es incierta:
el StraightPath se crea sin conectar (si StraightPath requiriese 2 endpoints
como args de creacion, queda documentado como limite en el resultado).
NO crea Dispatcher/TE.

Args:
    name_prefix: prefix for ControlPoint names (default AGV -> AGVCP1..N).
    n_points: 2-10 control points (default 4).
    spacing: rectangle side in meters (default 8).
    x, y, z: origin corner of the rectangle.
    overwrite: destroy previous <prefix>CPi / <prefix>Path12 first when they
        already exist (default False: reject without writing anything).

Siguiente paso (red AGV, marcador # AGV-red): flexsim_agv_link_path une dos
extremos (path-path o ControlPoint-path) con objectconnect verificado por
_flow_path; flexsim_agv_route despacha una sola TS con un TRAVEL por stop.
Guia completa de la red en el docstring de flexsim_agv_route.

Returns JSON {control_points, paths, limit, note} (+ errors / error).
Limit: red completa y tasksequences AGV aun no verificadas.
flexsim_link_transporterB

Link a transporter to a dispatcher via resource (S) connections (window backend only).

Cadena minima documentable: contextdragconnection(transporter, dispatcher, "S")
(el transporter pide trabajo al dispatcher, analogo a Processor->Operator
verificado). Si processor dado, ademas contextdragconnection(processor,
dispatcher, "S") con kind resource y verificacion de cada enlace con
centerObjects. Topologia Dispatcher movil no verificada end-to-end
(sin TaskSequence de prueba).

Args:
    dispatcher: dispatcher object name.
    transporter: transporter object name (e.g. TaskExecuter/AGV).
    processor: optional processor linked to the dispatcher as resource.
    verify: check centerObjects (default True).

Returns JSON {links: [{from, to, verified}], warning}.
Warning: topología Dispatcher móvil no verificada end-to-end (sin TaskSequence de prueba).
flexsim_processflow_inventoryA

List ProcessFlow activities read-only (window backend only, never creates).

Solo lectura (a diferencia de flexsim_processflow_ping que puede crear):
lista subnodos del ProcessFlow + por cada actividad su clase y conteo de
conexiones (inObjects/outObjects length). Nunca crea nada.

Args:
    name: ProcessFlow object name (default ProcessFlow1).

Returns JSON {exists, activities: [{name, class, in_count, out_count}], note}.
flexsim_pf_add_activityA

Add one ProcessFlow activity object at model level (window backend only).

Vecinas: flexsim_processflow_ping (crea ProcessFlow::ProcessFlow + inventario)
y flexsim_processflow_inventory (solo lectura: clase + in/out counts).

Catalogo minimo (NO verificado: construir actividades no se probo,
AGENTE_FLEXSIM.md sec 3c): solo Object.create("ProcessFlow::ProcessFlow")
esta verificado (sin prefijo "ProcessFlow" da `is not a valid class name`).
Por eso activity_class es LIBRE (default "ProcessFlow::Delay") y estos
candidatos llevan prefijo "ProcessFlow::" por analogia, pendiente de
verificacion: "ProcessFlow::Delay", "ProcessFlow::Source",
"ProcessFlow::Sink", "ProcessFlow::Process" (o sin prefijo? incierto).

La lista exacta sale de la biblioteca via:
  execute_script: treenode g = library().find("?processflow"); string s = "";
    for (int i = 1; i <= g.subnodes.length; i++) s = s + g.subnodes[i].name + ","; return s;
(patron AGENTE_FLEXSIM.md sec 4 para "?fixedresources"; grupos verificados
incluyen "processflow"). flexsim_list_tree lista el arbol del modelo
(path="Model"), no la biblioteca, asi que para clases use execute_script.

Metodo (solo comandos verificados): Object.create(activity_class) +
o.name + o.setLocation(x, y, 0) + return o.name (receta AGENTE_FLEXSIM.md
sec 9). NO parenta al flow: en este repo no hay forma verificada de mover
un objeto como subnodo del ProcessFlow (grep: solo Tools.create,
createcopy bajo Tools/GlobalTables y Object.create; ningun movetomodel/
parent verificado), asi que se crea a nivel modelo y se devuelve
attached:false.

Args:
    flow: ProcessFlow existente (debe existir via Model.find).
    activity_class: clase libre (default "ProcessFlow::Delay").
    name: nombre unico de la actividad.
    x, y: posicion (z=0).

Returns JSON {activity, flow, activity_class, attached:false,
    note:"attach al flow pendiente de verificacion"}.
Limite: attached siempre false; el attach al flow queda pendiente.
flexsim_pf_connectA

Connect two ProcessFlow activities with a token line (window backend only).

Los paths de actividades dentro del flow son inciertos, asi que primero
RESUELVE ambos (solo lectura, sin escribir): Model.find("flow/src"),
Model.find("src") (nivel modelo, donde flexsim_pf_add_activity crea) y
scan de subnodos del flow por nombre. Si alguno no resuelve, devuelve error
con los candidatos probados y no escribe nada.

Si resuelve: contextdragconnection(Model.find(from), Model.find(to), "A")
(forma no obsoleta de flujo, como flexsim_connect_objects) y verificacion
outObjects via _last_link (guard length==0, mismo helper de
flexsim_connect_objects).

Args:
    flow: ProcessFlow contenedor (para candidatos "flow/actividad").
    source: actividad origen (nombre corto).
    target: actividad destino (nombre corto).
    verify: comprobar outObjects (default True).

Returns JSON {resolved_from, resolved_to, verified} (+ last_out / error).
Limite: la semantica token de ProcessFlow no esta verificada end-to-end
(sin corrida de tokens de prueba); solo se verifica el enlace outObjects.
flexsim_ensure_parameterA

Ensure a model parameter exists, creating it when missing (window backend only).

Replicates the VERIFIED pattern of AGENTE_FLEXSIM.md sec 3c over
Tools/ParameterTables/Parameters>variables/parameters: Table addRow +
cell(k,1).value=name + cell(k,2).value=value. When the parameter already
exists (Model.parameters.names() contains it) only the value is updated.
Returns JSON {name, created, value, backend}. Validates rectangular
scalar values and types (number|string). Never touches
PerformanceMeasureTables.

Args:
    name: Parameter name (only letters, digits, _).
    value: New value (number or string, must match type).
    type: number (default) or string.

Limits: needs the Toolbox ParameterTables node in the model; string cells
    with '|' or ';' are stored verbatim (no split here, unlike table_read).
flexsim_dashboard_createA

Create one dashboard/chart window object (window backend only).

Solo creacion + posicion (sin configuracion de series/datos: no hay
setProperty de charts verificado en el repo, asi que no se inventa API).

Clases creables via Object.create verificadas en AGENTE_FLEXSIM.md sec 4:
StatChart, GanttChart, Recorder. Dashboard se acepta por patron pero queda
pendiente de verificacion (si la clase no existe FlexSim devuelve FAIL:create).

Metodo (receta AGENTE_FLEXSIM.md sec 9): Model.find duplicado + destroyobject
si overwrite + Object.create(kind) + o.name + o.setLocation + return o.name.

Args:
    kind: StatChart (default), GanttChart, Recorder o Dashboard.
    name: nombre unico (solo letras, digitos, _).
    x, y, z: posicion en metros (default 0, 0, 0).
    overwrite: si True destruye el objeto previo con el mismo nombre.

Returns JSON {name, kind, created, note} (o {name, kind, created:false, error}).
flexsim_dashboard_listA

List chart/dashboard/recorder objects as JSON (window or Python backend).

Solo lectura (dual via run_script): recorre model().subnodes con
getname(classobject(o)) + o.location (patron de flexsim_inspect_model) y
filtra en Python por clase conteniendo Chart|Dashboard|Recorder|Plot|
Histogram, mas filtro opcional pattern (subcadena sobre name y class).
Sin resultados devuelve lista vacia, no error.

Args:
    pattern: subcadena opcional para filtrar name/class (default "" = sin filtro).
flexsim_agv_connectA

Connect two AGV segments with objectconnect (window backend only).

Une dos tramos AGV (StraightPath/CurvedPath) o un ControlPoint a un path
con objectconnect (el mismo comando verificado para cintas en
flexsim_add_conveyor_leg; si FlexSim crea objetos intermedios, la
verificacion lo reporta via el path completo).

Vecina: flexsim_add_agv_loop crea ControlPoints + un StraightPath piloto
sin conectar; esta tool cierra ese gap conectando dos tramos ya creados.
NO crea Dispatcher/TE ni tasksequences.

Args:
    path_from: tramo AGV origen (o ControlPoint origen).
    path_to: tramo AGV destino.
    verify: caminar outObjects con BFS (_flow_path) buscando path_to.

Returns JSON {from, to, linked, path | last_out, note}.
Limit: red AGV completa/tasksequences no verificadas.
flexsim_experimenter_statusA

Report Experimenter existence + scenarios read-only (window or Python backend).

SOLO LECTURA (nunca escribe): detecta el Experimenter via Model.find(name)
y/o scan de objetos del modelo por clase conteniendo "Experiment"
(solo model().subnodes + getname(classobject()), sin comandos nuevos).
Lista escenarios como subnodos del Experimenter y cuenta jobs solo si hay
un subnodo visible con "job" en el nombre (si no, jobs=0). NUNCA crea,
conecta ni lanza nada.

Args:
    name: nombre del Experimenter (default Experimenter1).

Returns JSON {exists, name, scenarios, jobs, note}
(+ hint cuando no existe: crearlo a mano en Toolbox).
flexsim_run_transportA

Dispatch a loaded trip: TRAVEL pickup, LOAD item, TRAVEL dropoff, UNLOAD (window backend only).

API (fuente): createemptytasksequence(dispatcher, priority, preempting)
+ inserttask(ts, type, involved1, involved2) + dispatchtasksequence(ts),
firmas y ejemplos de Commands.xml:6854-6858, :7951-7955 y :7094-7099;
TASKTYPE_LOAD/TRAVEL vistos en XML:7751/:7955, UNLOAD solo en headers
locales FlexsimDefs.h:210 / allobjects.h:290. Stats del dropoff segun
AGENTE_FLEXSIM.md sec 7. Corrida opcional con el patron de
flexsim_run_to_time (Run Speed slider + stoptime + applicationcommand run
+ espera getrunstate==0). # DLV-delivery

Args:
    transporter: TaskExecuter/Transporter que ejecuta (dueno de la TS).
    item_from: flowitem a mover (Model.find debe resolverlo; para ver los
        items dentro de una estacion usar flexsim_list_tree sobre ella).
    pickup: objeto/estacion donde se carga.
    dropoff: objeto/estacion donde se descarga (se leen sus stats).
    priority: prioridad de la TS (default 1).
    run_after: si True, tras dispatch OK corre el modelo una duracion
        run_seconds y confirma por stats (default False).
    run_seconds: duracion simulada a correr tras despachar (default 600).
    run_speed: Run Speed slider para la corrida (default 1000).

Returns JSON {dispatched, delivered (bool|null si run_after=false),
    transporter, item_from, item_path, pickup, dropoff, priority,
    flexsim_result, dropoff_input_before, dropoff_input_after,
    model_time_end, flexsim_console, limits} o {dispatched:false, error,
    ...} con el error real de FlexSim, sin mentir.
Limits: semantica LOAD/UNLOAD (involved1=item, involved2=estacion) no esta
    en Commands.xml (remite al QuickReference); dispatched=true es TS
    aceptada, no entrega confirmada (requiere correr el modelo).
Receta de verificacion (humano): flexsim_reset + linea con items
    (Source>Queue>...) + flexsim_run_transport con run_after=true
    (run_seconds/run_speed) + flexsim_get_statistics sobre el dropoff
    (input.value debe incrementar; delivered refleja eso).
flexsim_pf_attachA

Move a model-level activity INSIDE its ProcessFlow (window backend only).

Vecinas leidas antes de escribir (sin duplicar nombres de inputs):
flexsim_pf_add_activity (crea a nivel modelo, attached:false),
flexsim_pf_connect (resuelve flow/actividad + token line "A") y
flexsim_processflow_inventory (solo lectura: clase + in/out counts).

Metodo (solo ventana): moveobject(object, containerobject) de la
referencia FlexSim BasicModelingFunctions ("moves the object out of its
current container into its new container") para mover la actividad
creada por pf_add_activity dentro del flow. Verifica con el parent:
Model.find("flow/activity") != NULL tras mover.

Args:
    flow: ProcessFlow contenedor (debe existir via Model.find).
    activity: actividad a mover (nombre corto, creada a nivel modelo).

Returns JSON {activity, flow, attached, parent_path} (+ error real
cuando attached=false, sin mentir).
Limite: la semantica token de ProcessFlow no esta verificada end-to-end.
flexsim_pf_run_tokensA

Observe ProcessFlow state across a short reset+run (window backend only).

Vecinas leidas antes de escribir: flexsim_processflow_inventory
(inventory-like script por subnodos del flow) y flexsim_run_to_time
(flujo ventana: stoptime + applicationcommand run + espera
getrunstate==0 con deadline). SOLO LECTURA + RESET/RUN: no construye
nada (no crea actividades ni inyecta tokens).

Flujo minimo inline en esta tool (sin duplicar helpers): conteo antes
via subnodos del flow, resetmodel, Run Speed 1000 via
node("MAIN:/project/exec/step").value, stoptime(60) +
applicationcommand("run"), espera getrunstate==0 con deadline, conteo
despues.

Args:
    flow: ProcessFlow existente (si no existe, error claro).
    arrivals: referencia esperada de arribos (default 5, 1-100; solo
        referencia, no se inyecta nada: solo lectura).

Returns JSON {flow, before, after, arrivals_observed, note} con
note="tokens inferidos por cambio de estado, semántica token no
verificada a fondo". before/after son conteos de subnodos del flow;
arrivals_observed = after - before (puede ser 0: los tokens runtime no
siempre cambian subnodes).
flexsim_chart_inspectA

Inspect a chart's internal tree structure, read-only (window or Python backend).

Solo lectura (dual via run_script, como flexsim_dashboard_list): vuelca la
estructura interna del chart con primitivas verificadas (ver bloque P3):
subnodos directos (nombre+clase, 1 nivel), variables (hijos de `>variables`
como nombre=value via treenode.value, hasta 30) y, por cada subnodo cuyo
nombre contenga data|serie|dataset, sus hijos.

Vecinas: flexsim_dashboard_create crea el chart; flexsim_dashboard_list lo
localiza; esta tool descubre de donde lee antes de bindear (ver
flexsim_chart_bind_stat).

Args:
    name: nombre del chart (Model.find debe resolverlo).

Returns JSON {name, kind, variables, subnodes, data_nodes, note}. Si el
chart no existe devuelve error claro (sin JSON). No escribe nada.
Limite: la semantica de cada subnodo (que serie alimenta) solo se conoce
viendo el modelo real; lo no verificable queda como limite, no se inventa.
flexsim_chart_bind_statB

Request a chart/statistic binding: inspect only, never writes (window or Python backend).

Camino honesto y reversible (ver bloque P3): grep del 2026-09-26 en repo y
Commands.xml (collector 0 hits, addcolumn 0, addrow solo Table.addRow
deprecated XML:9011, Chart 5 hits sin binding) NO hallo patron verificable
de binding por Statistics Collector, asi que NO escribe en ningun nodo del
chart (semantica desconocida; escribir titulo/nota seria decorativo y
confunde) y devuelve el mapa de nodos candidatos (misma inspeccion de R1)
+ guia de pasos manuales en GUI + "binding automático no implementado".

Args:
    chart: chart destino (Model.find debe resolverlo).
    statistic: Object.stats.<group>.<field>, debe estar en ALLOWED_STATS
        (p. ej. "content.value"; ver AGENTE_FLEXSIM.md sec 7).
    source_object: objeto que produce el dato (Model.find debe resolverlo).

Returns JSON {chart, statistic, source_object, bound:false, inspected:{kind,
subnodes, variables, data_nodes}, did:[...], did_not:[...], manual_steps,
limits, note} donde did/did_not dicen que hizo y que no. Si chart o
source_object no existen, o statistic es desconocido, devuelve error claro.
flexsim_agv_link_pathA

Link two AGV endpoints with objectconnect (window backend only).

Une point_a -> point_b, donde cada extremo puede ser un AGV::StraightPath /
CurvedPath o un AGV::ControlPoint (p. ej. cerrar el loop de
flexsim_add_agv_loop, o atar un ControlPoint a su path).

Fuente / honestidad: objectconnect es el comando verificado para flujo
general (AGENTE_FLEXSIM.md sec 5) y para cintas (AGENTE_FLEXSIM.md sec 3c,
receta de flexsim_add_conveyor_leg); sobre objetos AGV su efecto NO esta
pre-verificado, asi que esta tool lo prueba en codigo y lo verifica con
_flow_path (BFS de outObjects ya usado por flexsim_agv_connect y
flexsim_add_conveyor_leg, que reporta la cadena real incluso con nodos
intermedios). No se inventa ningun comando AGV especifico.

Args:
    point_a: ControlPoint o path origen (Model.find debe resolverlo).
    point_b: ControlPoint o path destino (Model.find debe resolverlo).
    verify: caminar outObjects con BFS (_flow_path) buscando point_b.

Returns JSON {from, to, linked, path | last_out, note}.
note: "tasksequences sobre la red: flexsim_run_transport".
flexsim_agv_routeA

Dispatch one AGV route: single TS with one TRAVEL per stop, or full mission with load/unload (window only). # AGV-mision

Receta mision completa (AGV-mision): 1) flexsim_add_agv_loop crea el loop
(CPs + StraightPath piloto sin conectar); 2) flexsim_agv_link_path /
flexsim_agv_connect une tramos y CPs con objectconnect verificado por
_flow_path (BFS de outObjects); 3) esta tool con solo stops mueve el
transporter en vacio por la red (un TRAVEL por stop, UNA sola TS); 4) con
load_item+load_from+unload_to anade en LA MISMA TS: TRAVEL stops... +
LOAD(item, load_from) + TRAVEL unload_to + UNLOAD(item, unload_to) con un
solo dispatch; 5) con confirm=true + run_after=true corre hasta run_to_time
y confirma por stats (delivered = stats.input de unload_to > before).

API (fuente, la misma ya usada en flexsim_run_transport y _dispatch_trip,
sin inventar task types: solo TRAVEL/LOAD/UNLOAD): createemptytasksequence
(dispatcher, priority, preempting) (Commands.xml:6854-6858, ej. XML:6858) +
inserttask(ts, type, involved1, involved2) (Commands.xml:7951-7955, ej.
XML:7955 "inserttask(ts, TASKTYPE_TRAVEL, current, NULL);") +
dispatchtasksequence(ts) (Commands.xml:7094-7099, ej. XML:7099);
TASKTYPE_TRAVEL=8 a objeto (FlexsimDefs.h:215; XML:7955/:6498),
TASKTYPE_LOAD (XML:7751), TASKTYPE_UNLOAD solo en headers locales
(FlexsimDefs.h:210 / allobjects.h:290), PREEMPT_NOT en FlexsimDefs.h:262.
Item por las 3 vias de flexsim_run_transport (tal cual, load_from/item,
scan de hijos por nombre); stats con `Object k = Model.find(..);
return k.stats.input.value;` (AGENTE_FLEXSIM.md sec 7); corrida con el
patron de flexsim_run_to_time (Run Speed slider
node("MAIN:/project/exec/step").value = 1000 + stoptime(t) +
applicationcommand("run") + espera getrunstate()==0 + return Model.time,
sin resetmodel porque borraria la TS en curso).

Args:
    transporter: TaskExecuter/AGV que ejecuta (dueno de la TS).
    stops: 2-6 objetos/ControlPoints en orden de visita.
    priority: prioridad de la TS (default 1).
    run_after: si True, corre el modelo hasta run_to_time tras despachar.
    run_to_time: segundos simulados para la corrida (default 120).
    load_item: flowitem a mover (None = ruta solo-stops; con mision los
        tres juntos son obligatorios).
    load_from: objeto/estacion donde se carga (involved2 del LOAD).
    unload_to: objeto/estacion donde se descarga (TRAVEL destino +
        involved2 del UNLOAD; se leen sus stats).
    confirm: si True (y run_after=true) confirma delivered por stats.
    confirm_timeout_s: presupuesto wall-clock para la espera getrunstate
        cuando confirm=true (default 600; el target simulado sigue siendo
        run_to_time).

Returns JSON {dispatched, transporter, stops, priority, flexsim_result,
console, delivery} (+ model_time y note="ruta ejecutada, verificar stats"
con run_after=true). delivery es null en ruta solo-stops; en mision es
{item, item_path, load_from, unload_to, confirm, delivered (bool|null),
input_before, input_after}.
Limites (AGV-mision): TRAVEL a objeto (inserttask(ts, TASKTYPE_TRAVEL,
destino, NULL), XML:7955) es el que usa la red AGV; TRAVELTOLOC=9 es a
coordenadas (no red) y FREETRAVEL/TRAVELTO no existen como TASKTYPE: no se
usan. Semantica LOAD/UNLOAD (involved1=item, involved2=estacion) no esta en
Commands.xml (remite al QuickReference): si FlexSim la rechaza se devuelve
su error real con dispatched:false. dispatched=true es TS aceptada, no ruta
completada ni entrega. delivered=true es input de unload_to incrementado
tras correr (evidencia, no trazabilidad del item); sin corrida (run_after
false) o sin confirm no se afirma nada (delivered null). Mision parcial
(alguno de load_item/load_from/unload_to sin los otros dos) es error sin
despacho.
Ejemplo multi-waypoint 3 CPs + mision con confirm (sintaxis exacta # T5):
    {"params": {"transporter": "AGV1", "stops": ["CP1", "CP2", "CP3"],
        "load_item": "Box1", "load_from": "Queue1", "unload_to": "Sink1",
        "confirm": true, "run_after": true, "run_to_time": 660}}
flexsim_agv_deliverA

Deliver one item on the AGV network: TRAVEL pickup, LOAD, TRAVEL dropoff, UNLOAD + run (window only). # AGV-dlv

Fuente (sin inventar comandos ni task types): createemptytasksequence
(Commands.xml:6854-6858, XML:6855/6858) + inserttask (Commands.xml:7951-7955,
ej. XML:7955 "inserttask(ts, TASKTYPE_TRAVEL, current, NULL);") +
dispatchtasksequence (Commands.xml:7094-7099, ej. XML:7099); TASKTYPE_TRAVEL=8
(a objeto, el de red AGV) vs TRAVELTOLOC=9 (a coordenadas, XML:6921-6927, no red)
en FlexsimDefs.h:215-216 / allobjects.h:295-296 (catalogo AGV-dlv A1 arriba);
TASKTYPE_LOAD en XML:7751, UNLOAD solo en FlexsimDefs.h:210 / allobjects.h:290,
PREEMPT_NOT en FlexsimDefs.h:262. Despacho via _dispatch_trip (A2, misma TS de
flexsim_run_transport bloque P1). Patrones verificados del repo: require_gui /
run_script / fs_string / _fs_literal / check_script_safety / format_error;
NULL-check `treenode n = Model.find(..); return (n == NULL ? 0 : 1);`
(flexsim_experimenter_status); item por paths candidatos tal cual,
pickup/item y scan hijos (flexsim_run_transport); red con _flow_path BFS
outObjects (flexsim_agv_connect/_last_link); stats `Object k = Model.find(..);
return k.stats.input.value;` (AGENTE_FLEXSIM.md sec 7); corrida sin reset con
Run Speed slider + stoptime + applicationcommand run + espera getrunstate==0
(flexsim_run_to_time / flexsim_run_transport, sin resetmodel porque borraria la TS).

Args:
    traveler: TaskExecuter/AGV que ejecuta (dueno de la TS).
    item_from: flowitem a mover (Model.find debe resolverlo; ver items con
        flexsim_list_tree sobre la estacion).
    pickup: objeto/estacion donde se carga.
    dropoff: objeto/estacion donde se descarga (se leen sus stats).
    control_points: 0-6 ControlPoints/path de la red (default []); si no vacia
        se verifica con _flow_path que pickup alcanza el primer CP (reporta,
        no bloquea).
    run_seconds: duracion simulada a correr tras despachar (default 600, >0).
    run_speed: Run Speed slider para la corrida (default 1000, >0).
    priority: prioridad de la TS (default 1).

Returns JSON {dispatched, delivered, traveler, item_from, item_path, pickup,
    dropoff, priority, flexsim_result, dropoff_input_before, dropoff_input_after,
    model_time_end, flexsim_console, network_checked:{...}, limits[]} (+ error
    real cuando dispatched=false o sin confirmar entrega, sin mentir).
Limits: semantica LOAD/UNLOAD (involved1=item, involved2=estacion) no esta en
    Commands.xml (remite al QuickReference); dispatched=true es TS aceptada, no
    entrega; delivered=true es input del dropoff incrementado tras correr
    (evidencia, no trazabilidad del item); network_checked solo reporta.
Presupuesto TE (ajusta el OPERADOR # T5): run_seconds >= distancia_total_m /
    velocidad_TE_mps + 30% margen; velocidad_TE_mps se lee en la GUI del
    TaskExecuter (propiedades del TE), distancia_total_m es el recorrido
    pickup->dropoff por la red AGV.
flexsim_experimenter_runB

Report Experimenter scenarios without launching (no verified starter).

SOLO LECTURA (nunca escribe): R1 no hallo comando de arranque en
Commands.xml (solo resetexperiment XML:5633-5636 y endreplication
XML:9572-9576, ninguno lanza), asi que esta tool NO ejecuta
resetexperiment/endreplication ni toca nodos del Experimenter en
escritura. Reutiliza el patron de la vecina flexsim_experimenter_status
(Model.find + scan por clase conteniendo "experiment" + subnodos) para
listar escenarios y devolver el camino honesto alternativo. Paso previo
recomendado: flexsim_experiment_plan (lee variables por escenario para
flexsim_run_experiments).

Args:
    name: nombre del Experimenter (default Experimenter1).
    timeout_s: deadline aceptado por firma (60-3600, default 600); sin
        arranque verificado no hay poll que esperar, se devuelve tal cual.

Returns JSON {started:false, reason, name, exists, scenarios, workaround,
    timeout_s, note} (y hint cuando no existe: crearlo a mano en Toolbox).
flexsim_collector_createA

Create one Statistics Collector object (window backend only).

Camino SEGURO y honesto (ver bloque DB-bind): Object.create + name unico +
setLocation + return verificado (receta de flexsim_dashboard_create,
AGENTE_FLEXSIM.md sec 9). La clase del grupo statistics NO esta verificada
en repo ni Commands.xml (grep 2026-09-26: collector 0 hits), asi que
collector_class es libre (default "StatisticsCollector"): si Object.create
falla se devuelve el error real de FlexSim sin mentir ni inventar clases
candidatas.

Args:
    name: nombre unico (solo letras, digitos, _).
    collector_class: clase libre del grupo statistics (default
        "StatisticsCollector", no verificado).
    x, y, z: posicion en metros (default 0, 0, 0).
    overwrite: si True destruye el objeto previo con el mismo nombre.

Returns JSON {name, class, created} o {name, class, created:false, error}
con el error real + hint para listar candidatas a mano por
flexsim_execute_script con library().find("?statistics").
Limite: solo creacion+posicion verificadas; configuracion interna del
collector no verificada (ver flexsim_collector_record).
flexsim_collector_recordA

Diagnose a collector's structure, read-only: never samples nor writes (dual backend).

Tool de DIAGNOSTICO (no promete muestreo): NO escribe configuracion interna
del collector (semantica desconocida, bloque DB-bind). Reusa el helper
existente _chart_inspect_map (misma inspeccion de flexsim_chart_inspect)
para volcar data_nodes del collector si existe.

Args:
    collector: collector a inspeccionar (Model.find debe resolverlo).
    source_object: objeto que produciria el dato (Model.find debe resolverlo).
    statistic: Object.stats.<group>.<field>, validado contra ALLOWED_STATS
        (ver AGENTE_FLEXSIM.md sec 7); no bloquea, se reporta como
        statistic_validated true/false.
    event: evento nominal (default "OnReset"), solo informativo: no se
        conecta nada.

Returns JSON {recorded:false, collector, source_object, statistic, event,
statistic_validated, collector_structure:{kind, subnodes, variables,
data_nodes}, suggestion, note}. recorded siempre false. Si collector o
source_object no existen, error claro con recorded:false. No escribe nada.
Limite: de donde lee/muestrea cada collector solo se sabe viendo el modelo
real; lo no verificable queda como limite.
flexsim_chart_snapshotA

Sample real time series to feed a chart outside (window backend only).

"Binding" honesto y funcional que SI podemos verificar (ver bloque DB-bind):
NO toca el chart GUI (semantica de sus series no verificada); corre el
modelo (reset + run_to_time) y muestrea get_statistics (_collect_statistics,
Object.stats.<group>.<field> segun AGENTE_FLEXSIM.md sec 7) en N=5 instantes
intermedios (T/5, 2T/5...T).

Vecinas: metricas validadas contra ALLOWED_STATS (como flexsim_chart_bind_stat);
objetos verificados con Model.find (como flexsim_experimenter_status); corrida
con el patron de flexsim_run_to_time (Run Speed slider + stoptime + run +
espera getrunstate()==0).

Args:
    chart: nombre del chart destino (solo etiqueta de salida, no se toca).
    statistics: 1-8 metricas Object.stats.<group>.<field> en ALLOWED_STATS.
    objects: 1-10 nombres de objetos (Model.find debe resolverlos).
    run_to_time: segundos simulados totales T (default 300).
    run_speed: Run Speed slider (window only, default 1000).

Returns JSON {chart, series:{obj:{metric:[v x5]}}, times:[t x5], note}.
Si metricas u objetos fallan, error claro en JSON sin escribir ni correr nada.
Limite: serie real para graficar fuera; el chart GUI se alimenta a mano.
flexsim_fluid_tankA

Create one FluidTank in the FlexSim window (window backend only).

Clase verificada: Object.create("FluidTank") (AGENTE_FLEXSIM.md sec 4;
crea ademas FluidTicker). Con prefijo ("Fluid::FluidTank") NO existe
(misma seccion). Otras clases de fluidos existen en la biblioteca pero
no estan probadas: listarlas a mano por flexsim_execute_script con
library().find("?fluid") (patron AGENTE_FLEXSIM.md sec 4 para
"?fixedresources"; grupo "fluid" listable segun sec 4).

Metodo (receta AGENTE_FLEXSIM.md sec 9): Object.create + name unico +
setLocation + return o.name verificado.

Propiedades: MaxContent esta verificado SOLO para Processor/Queue
(AGENTE_FLEXSIM.md sec 6); para tanques NO se asume. Si se pasan
initial_content/max_content se intenta o.setProperty + o.getProperty
roundtrip ("MaxContent" para max_content, "InitialContent" para
initial_content, ambos NO verificados en FluidTank) y si el valor leido
no coincide se devuelve warning "propiedad no efectiva en FluidTank"
SIN fallar la creacion. Propiedades verificadas en el repo (sec 6):
InterArrivalTime/ProcessTime/SetupTime/MaxContent/BatchSize; cualquier
otra solo via roundtrip con aviso.

Args:
    name: nombre unico (solo letras, digitos, _).
    x, y, z: posicion en metros (default 0, 0, 0).
    initial_content: contenido inicial deseado (opcional, roundtrip).
    max_content: capacidad deseada (opcional, roundtrip).
    overwrite: si True destruye el objeto previo con el mismo nombre.

Returns JSON {name, created, level, note} (+ warnings/error). level es
{initial_content, max_content} con los valores roundtrip que resolvieron
a numero (None cuando no se pidieron o no fueron efectivos); level es
null cuando no se pidio ninguno.
flexsim_person_createA

Create one people object in the FlexSim window (window backend only).

Via recomendada ahora (# LIB-people): flexsim_library_list +
flexsim_create_any; esta tool queda como caso simple de una sola clase.

OJO: "Person" NO esta verificado como clase creable. El grupo ?people
es listable (AGENTE_FLEXSIM.md sec 4) pero sin prueba de creacion en el
repo. Por eso person_class es LIBRE (default "Person"): se intenta
Object.create(person_class) + name + setLocation + return verificado
(receta AGENTE_FLEXSIM.md sec 9) y si FlexSim responde error/nombre
distinto se devuelve created:false con el error real, sin mentir exito.

Clases candidatas: grep 2026-09-26 de people/Person/Staff/Patient en
AGENTE_FLEXSIM.md y mcp_server/flexsim_mcp.py solo hallo el grupo
`people` listable (sec 4), sin clases candidatas verificadas; por eso
NO se sugieren 2-3 clases aqui (no hay de donde sacarlas sin inventar).

Para listar candidatas reales hacerlo a mano por flexsim_execute_script
con: treenode g = library().find("?people"); string s = ""; for (int i
= 1; i <= g.subnodes.length; i++) s = s + g.subnodes[i].name + ", ";
return s; (patron AGENTE_FLEXSIM.md sec 4 para "?fixedresources").

Args:
    person_class: clase libre del grupo people (default "Person",
        no verificado).
    name: nombre unico (solo letras, digitos, _).
    x, y, z: posicion en metros (default 0, 0, 0).
    overwrite: si True destruye el objeto previo con el mismo nombre.

Returns JSON {name, class, created} o {name, class, created:false,
error, hint} con hint "listar grupo ?people con
library().find(\"?people\") por execute_script". Nunca miente exito.
flexsim_fluid_levelA

Read a FluidTank level, read-only (window or Python backend via run_script).

Solo lectura (ventana o Python dual via run_script, como
flexsim_get_statistics): nunca escribe. Prueba en orden, con variable
tipada (AGENTE_FLEXSIM.md sec 7: Object k = Model.find(..)):
1. k.stats.content.value, 2. k.stats.content.average. NO inventa
formulas (stats.output.value menos stats.input.value PROHIBIDO).
Propiedades verificadas en el repo (sec 6):
InterArrivalTime/ProcessTime/SetupTime/MaxContent/BatchSize; el nivel
solo se lee de vuelta (roundtrip) como numero.

Args:
    name: tanque a leer (Model.find debe resolverlo).

Returns JSON {name, metrics, note} con metrics solo con los que
resolvieron a numero (p. ej. {"content.value": 12.5}). Si ninguno
resuelve, error claro con lo intentado (tried) sin ceros inventados.
flexsim_library_listA

List library class names in a group as JSON (window or Python backend). # LIB-people

Solo lectura (dual via run_script, como flexsim_dashboard_list): usa el
patron VERIFICADO de AGENTE_FLEXSIM.md sec 4:
treenode g = library().find("?group"); loop g.subnodes[i].name.

Vecinas leidas antes de escribir: flexsim_person_create (una sola clase
libre del grupo people) y flexsim_dashboard_list (filtra por clase en
Python con subcadena).

Grupos conocidos (AGENTE_FLEXSIM.md sec 4): fixedresources, taskexecuters,
conveyor, warehousing, people, travelnetworks, statistics, processflow,
fluid, AGV, astar, agent, Submodels.

Args:
    group: grupo de la biblioteca (default "people"). Validado con
        ^[A-Za-z0-9_]+$ (sin inyeccion: se cita con fs_string).
    pattern: subcadena opcional para filtrar clases (default "" = sin
        filtro). Subcadena Python `in`, sin regex inyectable (nunca va al
        FlexScript).

Returns JSON {group, count, classes:[...filtradas...]}. Si el grupo no
existe devuelve error claro con la lista de grupos conocidos.
flexsim_create_anyA

Create one object trying candidate classes in order (window backend only). # LIB-people

Solo ventana (require_gui). Prueba cada clase en orden con Object.create
+ name + setLocation + properties (roundtrip como create_objects:
o.setProperty + _fs_literal en el mismo script) + return o.name
verificado; al primer exito devuelve {name, class, created:true,
attempts:[{class, ok, detail}]}. Si todas fallan devuelve {created:false,
attempts:[...errores reales...], hint:"ver clases con
flexsim_library_list"}. Maneja duplicados como create_objects (SKIP sin
overwrite, destroy con overwrite).

Vecina leida: flexsim_create_objects (roundtrip de properties +
SKIP/destroy por duplicado) y flexsim_person_create (caso simple de una
sola clase; la via recomendada ahora es library_list + create_any).

Ejemplo people: candidates=["Person", "Staff", "Operator"] (Operator SI
funciona como fallback documentado, verificado en AGENTE_FLEXSIM.md sec 4;
Person/Staff no verificadas, se prueban en orden).

Args:
    candidates: 1-8 clases no vacias en orden de intento.
    name: nombre unico (solo letras, digitos, _).
    x, y, z: posicion en metros (default 0, 0, 0).
    properties: dict de setProperty (roundtrip como create_objects;
        ArrivalStyle/FlowItemClass bloqueados).
    overwrite: si True destruye el objeto previo con el mismo nombre.

Returns JSON {name, class, created:true, attempts} o {name,
created:false, attempts, hint} con errores reales de FlexSim, sin mentir.
flexsim_experiment_planA

Plan read-only de escenarios del Experimenter (pre-vuelo, sin lanzar).

SOLO LECTURA (nunca escribe): detecta el Experimenter via Model.find(name)
y/o scan por clase conteniendo "experiment" (patron de la vecina
flexsim_experimenter_status) y lista escenarios como subnodos. Por cada
escenario lee hasta 20 subnodos como nombre=valor texto
(`sc.subnodes[i].name + "=" + sc.subnodes[i].value`, como `>variables`
en _chart_inspect_map) via run_script dual (ventana o Python). Solo las
entradas con forma clara variable=numero van a params; las demas van a
unreadable con sus nombres. NUNCA crea, escribe ni lanza jobs.

Args:
    name: nombre del Experimenter (default Experimenter1).
    default_seeds: semillas para flexsim_run_experiments (default [101,202,303]).
    default_run_to_time: tiempo simulado para flexsim_run_experiments (default 600).
    default_run_speed: Run Speed slider para flexsim_run_experiments (default 1000).

Returns JSON {experimenter, exists, scenarios:[{name, params, unreadable,
    source:"variables"}], seeds, run_to_time, run_speed, ready, usage}
    (+ hint cuando no existe: crearlo a mano en Toolbox).
flexsim_table_from_snapshotA

Write a statistics snapshot into a GlobalTable (window backend only). # T6

Puente documentado snapshot -> GlobalTable -> (chart a mano). Esto NO bindea
el chart GUI (imposible verificado: grep 2026-09-26 en repo y Commands.xml no
hallo patron collector->chart; ver bloque P3 y flexsim_chart_bind_stat): solo
deja los numeros en Tools/GlobalTables/<table> para graficar fuera o alimentar
el chart a mano con los manual_steps de flexsim_chart_bind_stat.

snapshot: _collect_statistics una vez -> UNA fila de datos + header
  (metricas como columnas object_metric + time) via la misma rutina de
  escritura que flexsim_table_write (_write_table_block duplicado 15 lineas
  para no tocar su comportamiento).
timeseries: SIN reset (destruiria el estado): 3 snapshots consecutivos con
  stoptime now+60/120/180 desde el tiempo actual + columna time (header + 3
  filas). Corrida con el patron de flexsim_run_to_time/chart_snapshot
  (Run Speed 1000 + stoptime + applicationcommand run + espera getrunstate==0).

Args:
    table: GlobalTable destino (^[A-Za-z0-9_]+$, se crea si falta).
    statistics: GetStatisticsInput o None (mismos defaults: metricas basicas).
    mode: snapshot (default) o timeseries.

Returns JSON {table, rows, cols, mode} (+ error en JSON-siempre cuando falla).
Limite: el chart GUI se alimenta a mano (manual_steps de chart_bind_stat);
tablas >100x20 devuelven error (TABLE_MAX_ROWS/COLS de table_write).
flexsim_import_shapeA

Import a 3D shape file onto an existing object (window backend only).

Receta verificada en Commands.xml local (FlexSim 2026,
help/CommandReference/Commands.xml; usar EXACTAMENTE estos comandos y firmas):
- loadthreedobject(str file, str name)->num "Load a 3D model directly...
  recorded in Media List" (el cargador; paths con / simples).
  Ejemplo XML: loadthreedobject("c:/myshape.wrl","myshape")
- getshapeindex(str name) con DOBLE backslash obligatorio ("The file path
  name used in this command must use double backslashes as shown in the
  example"). Ejemplo XML: getshapeindex("fs3d\\Operator\\BasicOperator\\Chest.3ds");
- setobjectshapeindex(obj object, num index) + verificacion
  getobjectshapeindex(obj object). Ejemplo XML:
  setobjectshapeindex(item,getshapeindex("fs3d\\Processor.3ds"));
  ejemplo get: getobjectshapeindex(item);
- truncatemediapath(str path) para reportar como quedo referenciado
  ("If this file exists relative to the model directory, then the path
  will be truncated to be relative to the model directory").
- applyshapefactors(obj object) tras set de o.size.x/y/z ("This command
  applies all of the shape factors to the object if it has any").
  Ejemplo XML: applyshapefactors(item);

Vecinas leidas antes de escribir: flexsim_create_objects (patron
require_gui + Model.find NULL-check + Object.create/name/setLocation +
check_script_safety por script + format_error), _last_link (helpers de
verificacion por colecciones), fs_string (citado de strings via json.dumps,
que produce el \\ doble en fuente FlexScript a partir de un valor con
\ simple) y require_gui (solo ventana).

Contrato: EXITO devuelve JSON {object, file, copied, shape_name,
shape_index, assigned_index, media_ref, size} (+ "warning" solo cuando
getshapeindex difiere del idx de load). ERRORES son texto plano
"Error:/Not found:/Blocked for safety:" como el resto de tools simples
(NO {"error":...}); el exito es el unico caso JSON.

Flujo: 1) existe objeto (Model.find NULL-check). 2) si
copy_next_to_model: destino = dir(_gui_model_path o _current_model)+nombre;
si el archivo ya esta ahi, no copiar; shutil.copyfile (Python, no
FlexScript) + reporta copied:true/false. 3) loadthreedobject(abs con
slashes, shape_name) -> idx; si idx==0 -> error "no cargo (formato? ver
lagunas)" SIN seguir. 4) getshapeindex(abs con \) debe == idx (si
difiere, warning pero sigue con idx de load). 5) setobjectshapeindex +
getobjectshapeindex de vuelta == idx -> si no, error. 6) set_size_xyz
se IGNORA a propósito (o.size rompe el render de importadas; escalar
el archivo con utility/scale_stl.py).

IMPORTANTE: asignaciones por script son POR SESIÓN (verificado
2026-09-27: tras guardar+reabrir no persisten). Usar manifiesto +
flexsim_apply_shapes al abrir el modelo.

Lagunas: escala real por verificar visual; reset puede revertir
textura (shape no documentado).

Args:
    file_path: archivo existente; extension en {.skp,.fbx,.obj,.stl,.dae,
        .3ds,.wrl,.x,.blend,.usd,.jt,.igs,.stp,.step,.glb} case-insensitive.
    object: debe existir via Model.find, si no Not found.
    shape_name: default = stem sanitizado [A-Za-z0-9_].
    copy_next_to_model: default True.
    set_size_xyz: aceptado pero ignorado (ver arriba).
flexsim_apply_shapesA

Apply every entry of a shapes manifest JSON (window backend only).

Las asignaciones por script son POR SESIÓN (verificado 2026-09-27: tras
guardar+reabrir el índice vuelve a mapping default). El manifiesto
(ej. planta_sanjorge_v1_7.shapes.json) las versiona: al abrir el modelo,
correr esta tool y listo. Formato: {"shapes": [{object, file, shape_name?}]}
con file absoluto o relativo al manifiesto.

Args:
    manifest_path: JSON con clave "shapes" (lista de {object, file}).

Returns JSON {manifest, applied:[{object, shape_index, note}], errors:[...]}.
flexsim_paint_meatA

Pinta como carne los items que ya salieron de las estaciones indicadas.

Los items que estan POST-desencajado (en transit hacia picadora: queues Pie*,
cintas BeltDT*, y dentro de las picadoras) reciben la forma carne_1m5.stl y
color carne. Correr tras cada reset+run: el shape de items no persiste en el
.fsm (limitacion FlexSim: media por sesion, verificado 2026-09-27).

Args:
    from_objects: estaciones cuya salida se pinta (default DesT1/T2/L3).
    file_path: STL de carne (default: proyecto).
    shape_name: nombre de media para loadthreedobject (idempotente).
    color_rgb: color 0-1 (default rojo-marron carne).

Returns JSON {painted: {object: n}, shape_index, errors:[...]}.

Prompts

Interactive templates invoked by user choice

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription

No resources

TDQS

B3.1/5.0

Scored across 73 tools

Disambiguation2/5

Many tools overlap in reading statistics (flexsim_get_statistics, flexsim_fluid_level, flexsim_get_performance_measure, flexsim_chart_snapshot), creating objects (flexsim_create_objects, flexsim_create_any, flexsim_person_create, flexsim_dashboard_create, flexsim_collector_create, flexsim_fluid_tank), and connecting objects (flexsim_connect_objects, flexsim_attach_resource, flexsim_agv_connect, flexsim_pf_connect). Despite detailed descriptions and cross-references, the boundaries between tools remain fuzzy, forcing agents to parse experimental notes and backend hints to choose correctly.

Naming Consistency4/5

All tool names follow a consistent flexsim_ prefix and snake_case convention with descriptive terms. Minor deviations exist (e.g., verb-first vs. noun-first ordering, abbreviations like pf and agv), but there is no mixing of casing or chaotic naming styles.

Tool Count1/5

With 73 tools, the set is far beyond the typical 3-15 range and even exceeds the 50+ threshold for an extreme mismatch. Many tools are experimental, overlapping, or superseded, and do not all earn their place, indicating an overgrown surface for the server's purpose.

Completeness3/5

The surface covers many operations (create, connect, run, read statistics, export results), but notable gaps remain: no general delete tool, incomplete ProcessFlow building and chart binding, no verified experiment launching, and several read-only diagnostic stubs. These gaps will cause agents to work around missing functionality.

Maintenance

ActivityMaintained
ResponsivenessNo issues