pns-server MCP Server
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| APP_ENV | No | Environment (local / stage / prod) | local |
| DB_HOST | Yes | PostgreSQL host | |
| DB_NAME | Yes | PostgreSQL database name | |
| DB_PORT | Yes | PostgreSQL port | |
| REDIS_HOST | Yes | Redis host | |
| REDIS_PORT | Yes | Redis port | |
| REDIS_ENABLED | No | Enable Redis | true |
| OPENAI_API_KEY | Yes | OpenAI API key | |
| AGENT_LLM_MODEL | No | Model name | gpt-4o |
| ANTHROPIC_API_KEY | Yes | Anthropic API key | |
| LLM_BYOK_REQUIRED | No | Require user-provided LLM credentials | true |
| AGENT_LLM_PROVIDER | No | LLM provider (openai / anthropic / ollama) | openai |
| CREDENTIALS_CIPHER | No | Encryption mode (fernet / noop) | fernet |
| AUTH_JWT_SECRET_KEY | Yes | JWT signing key (min 32 chars) | |
| SIMULATION_REMOTE_URL | No | Optional external simulation backend | |
| CREDENTIALS_MASTER_KEY | Yes | 32-byte base64 key for Fernet |
Capabilities
Features and capabilities supported by this server
| Capability | Details |
|---|---|
| tools | {
"listChanged": false
} |
| prompts | {
"listChanged": false
} |
| resources | {
"subscribe": false,
"listChanged": false
} |
| experimental | {} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| create_networkA | Create a new empty Petri Net, clearing any previous network. Use this FIRST when building a new model from scratch. Args: name: Name for the new network (default: "PetriNet") |
| add_generatorA | Add a Generator (Source) pattern that creates tokens at regular intervals. Use this for arrival processes. Specify EITHER interval OR rate (not both): Using RATE (preferred for "N items per time unit"):
Using INTERVAL (for "every N time units"):
BATCH GENERATION (multiple items per arrival):
DISTRIBUTION SELECTION:
UNITS — TWO INDEPENDENT KNOBS:
Canonical translations:
Args: name: Name for this generator (e.g., "CustomerArrivals", "PartsInput") interval: Mean time BETWEEN arrivals in time_unit (use this OR rate, not both) rate: Number of arrivals PER time_unit (use this OR interval, not both) distribution: "exp", "norm", "unif", or "det" standard_deviation: Spread for "norm"/"unif" distributions (in deviation_unit, or time_unit if deviation_unit omitted) batch_size: Number of tokens per arrival (default: 1) time_unit: Unit for interval/rate — "s", "min", "h", "d". PASS THIS EXPLICITLY when the user describes the mean in non-second terms; the response echoes the resulting duration/rate in all four units so you can sanity-check. deviation_unit: Unit for standard_deviation. Pass ONLY when it differs from time_unit (e.g. mean in minutes, ± in seconds). |
| add_machineA | Add a Machine (Server) pattern with multiple processing channels. Use this for processing stations like machines, servers, workstations. UNITS — TWO INDEPENDENT KNOBS:
Args: name: Name for this machine (e.g., "Lathe", "Checkout", "Server") processing_time: Mean processing time in time_unit channels: Number of parallel channels distribution: Time distribution - "exp", "norm", "unif", or "det" standard_deviation: Spread for "norm"/"unif" (in deviation_unit, or time_unit if omitted) auto_connect: If True (default), auto-connects to previous output. Set to False for PARALLEL machines from same queue! time_unit: Unit for processing_time — "s", "min", "h", "d". deviation_unit: Unit for standard_deviation. Pass ONLY when it differs from time_unit. |
| add_controllerA | Add a Controller (Gate) pattern that allows flow only when a signal is present. The signal is read but NOT consumed (information link). Args: name: Name for this controller (e.g., "TrafficLight", "Permission") initial_signal: If True, the signal starts enabled (gate open) |
| add_assemblyA | Add an Assembly (Join) pattern that synchronizes multiple flows into one. Creates one new input place per slot named "{name}In{slot_name}" and a transition that fires when ALL slots have tokens. The caller must wire external sources to those slots via connect_patterns — the slot place_ids are returned in the response and in metadata. WEIGHTED INPUTS (for multiple tokens required):
Args: name: Name for this assembly (e.g., "CarAssembly", "SectionFill") inputs: Names for the input slots created by this assembly assembly_time: Mean firing time in time_unit distribution: Time distribution priority: Transition priority (higher = fires first in conflict) time_unit: Unit for time values — "s", "min", "h", or "d" (days) |
| add_routerA | Add a Router (Splitter) pattern that branches flow into multiple paths. Args: name: Name for this router (e.g., "QualityCheck", "LoadBalancer") routes: List of route configurations: For probability: [{"name": "Accept", "prob": 0.9}, {"name": "Reject", "prob": 0.1}] For priority: [{"name": "Primary", "priority": 2}, {"name": "Backup", "priority": 1}] PRIORITY RULE: higher "priority" number fires first (same as add_transition). The route with the highest priority number is tried first; lower numbers are fallbacks. strategy: "probability" for random split, "priority" for ordered selection |
| add_conveyorA | Add a Conveyor pattern with sequential processing stations. Every item is processed at every station (no bypass option). For stations with bypass logic, use add_conveyor_line instead. Args: name: Name for this conveyor (e.g., "AssemblyLine", "Pipeline") stations: List of station configs: [{"name": "Stage1", "time": 60.0, "distribution": "exp"}, ...] transport_time: Time to move between stations |
| add_conveyor_lineA | Add a COMPLETE conveyor line with Try-or-Bypass workstations. THIS IS THE PREFERRED TOOL for conveyor systems with bypass logic. Creates N workstations, all bypass connections, and return loop in ONE call. How it works:
Args: station_count: Number of workstations processing_time: Mean processing time at each station in time_unit distribution: Processing time distribution transport_time: Time to move between stations in time_unit return_delay: Time for items to return from last to first station in time_unit channels: Number of parallel resources per station station_prefix: Name prefix for stations (default: "Device") time_unit: Unit for all time values — "s", "min", or "h" |
| add_conveyor_workstationA | Add a SINGLE ConveyorWorkstation with Try-or-Bypass logic. For multiple workstations in a chain, prefer add_conveyor_line instead. Args: name: Name for this workstation (e.g., "Device1") processing_time: Mean processing time in time_unit distribution: Processing time distribution transport_time: Time to bypass to next station in time_unit channels: Number of parallel resources time_unit: Unit for time values — "s", "min", "h", or "d" (days) |
| connect_workstation_bypassA | Connect bypass output of one workstation to input of next. Prefer add_conveyor_line which creates all connections automatically. Args: source_workstation_name: Source workstation name target_workstation_name: Target workstation name |
| connect_return_loopA | Create return loop from last workstation back to first. Prefer add_conveyor_line which creates return loop automatically. Args: last_workstation_name: Last workstation name first_workstation_name: First workstation name return_delay: Time to return in time_unit (default: 5.0) time_unit: Unit for time values — "s", "min", "h", or "d" (days) |
| add_transport_agentA | Add a TransportAgent (cyclic resource) pattern. Models any resource that picks up, moves, releases, and returns: robots, forklifts, AGVs, cranes, couriers. Cycle: [Queue]+[Free] -> Grab -> Move -> Release -> Return -> [Free] Args: name: Name for this agent (e.g., "Robot1", "Forklift", "AGV") grab_time: Mean time to grab/pick item in time_unit grab_deviation: Standard deviation for grab in time_unit grab_distribution: Distribution for grab time move_time: Time to move WITH item in time_unit move_distribution: Distribution for move release_time: Mean time to release/place item in time_unit release_deviation: Standard deviation for release in time_unit release_distribution: Distribution for release return_time: Time to return EMPTY in time_unit (default: same as move_time) return_distribution: Distribution for return time_unit: Unit for all time values — "s", "min", or "h" |
| add_priority_choiceA | Add a PriorityChoice pattern for resource-dependent branching. Implements "do X if resources available, otherwise do Y" logic. Primary transition has higher priority and fires first if resources available. Args: name: Name for this choice point (e.g., "SectionFill") resource_place_ids: Resource requirements for primary path. Each dict: {"place_id": "p5", "weight": 10} primary_delay: Delay for primary/success transition in time_unit alternative_delay: Delay for alternative/fallback transition in time_unit primary_name: Name suffix for success output alternative_name: Name suffix for fallback output time_unit: Unit for time values — "s", "min", "h", or "d" (days) |
| add_mergeA | Add a Merge (OR-join / Funnel) pattern — combines multiple streams into one output. Fires as soon as ANY input has a token (unlike Assembly which waits for ALL). Creates one input place per stream named "{name}In{stream_name}". Wire each producer's output into the corresponding place via connect_patterns. Use this for:
Args: name: Name for this merge point (e.g., "OrderMerge") inputs: Stream names (e.g., ["Web", "Phone", "Fax"]) |
| add_duplicateA | Add a Duplicate (Broadcast) pattern — copies one token into N independent copies. One arriving token produces one token in each output place simultaneously. Use for parallel processing of the same item by independent services. Use this for:
Args: name: Name for this broadcast point (e.g., "OrderBroadcast") outputs: Output branch names (e.g., ["Warehouse", "Accounting", "Logistics"]) |
| add_multi_resourceA | Add a MultiResource (AND-Resource) pattern — op requiring multiple simultaneous resources. Seizes one token from each resource pool simultaneously and releases all upon completion. Unlike Machine (homogeneous channels), each pool can have a different name and capacity. Use this for:
Args: name: Instance name (e.g., "PaintBooth", "SurgeryRoom") resources: List of resource pool definitions. Each dict: {"name": str, "pool_size": int} Example: [{"name": "Worker", "pool_size": 3}, {"name": "Booth", "pool_size": 2}] processing_time: Mean processing time in time_unit distribution: Time distribution — "exp", "norm", "unif", "det" standard_deviation: Std dev for norm/unif (in time_unit) time_unit: Unit for time values — "s", "min", "h", or "d" |
| add_nonstationary_generatorA | Add a NonStationaryGenerator — arrival rate changes over cyclic time windows. Each time window has its own arrival rate. The schedule repeats cyclically. Use this for:
Args: name: Name for this generator (e.g., "CoffeeShop", "CallCenter") schedule: List of time windows. Each dict: {"duration": float (seconds), "rate": float (arrivals/sec)} Example: [{"duration": 7200, "rate": 0.33}, {"duration": 21600, "rate": 0.083}] batch_size: Tokens per arrival (default: 1) |
| connect_bounded_consumerA | Wire capacity recycling between a BoundedQueue and its consumer. Looks up the queue's capacity place by naming convention ('{queue_name}_Capacity') and the consumer's start transition ('{consumer_name}_Start' for Machine, '{consumer_name}_Do' for Assembly), then adds an arc consumer->capacity so each dequeue recycles a slot. Args: queue_name: Instance name passed to add_bounded_queue consumer_name: Instance name of the downstream consumer (Machine or Assembly) |
| add_fork_joinA | Add a ForkJoin (AND-split / AND-join) pattern for parallel branch execution. A single entity enters, splits into N independent parallel branches, and continues ONLY when ALL N branches have completed. Essential for modeling parallel workflows that must synchronize before proceeding. Difference from other patterns:
Args: name: Instance name (e.g., "PatientExam", "QualityGate") n_branches: Number of parallel branches (default: 2) branch_times: Mean processing time per branch in time_unit. Length must equal n_branches. Default: [30.0] * n_branches branch_distribution: Distribution for branches — "exp", "norm", "unif", "det" time_unit: Time unit for branch_times — "s", "min", "h" Returns branch IDs in the response for manual wiring when branches are processed by external patterns (retrial queues, machines, etc.). |
| connect_blocking_consumerA | Wire capacity recycling between a BlockingMachine and its downstream consumer. When the downstream machine starts processing an entity from the blocking machine's output buffer, one capacity slot is returned — allowing the blocking machine to release its next held entity. Call this AFTER connecting BlockingMachine output to downstream input. Args: blocking_machine_name: Instance name passed to add_blocking_machine downstream_name: Instance name of the downstream consumer (Machine, etc.) |
| add_resource_setA | Add a ResourceSet — entity seizes the first available resource from alternatives. Models "try resource A first; if busy, try B; if busy, try C." Resources are listed in preference order (index 0 = highest priority). Each resource has its own service time, channel count, and distribution. Args: name: Instance name (e.g., "MachinePool", "NursePool") resources: List of resource dicts, ordered by preference. Each dict: - "name": str (required) - "service_time": float (default 60.0, in time_unit) - "distribution": str "exp"|"norm"|"unif"|"det" (default "exp") - "channels": int (default 1) Example: [{"name": "FastMachine", "service_time": 30.0}, {"name": "SlowMachine", "service_time": 90.0}] standard_deviation: Global std dev fallback for norm/unif resources (in time_unit) time_unit: Time unit for service_time values — "s", "min", "h" |
| connect_wip_returnA | Wire the WIP return arc — recycle a WIP slot when an entity reaches the exit place. For every transition that already produces tokens into exit_place_id, an extra output arc to the controller's WIPSlots place is added. Tokens are NOT consumed — the entity continues to flow through the exit place to its downstream consumer. Call AFTER wiring the WIP-controlled section, so the producer transitions of exit_place_id exist. Args: wip_controller_name: Instance name passed to add_wip_controller exit_place_id: Output place ID of the last pattern in the controlled section |
| connect_schedule_gateA | Gate a machine with a shift schedule via READ arc. Adds a READ arc from the schedule's Active signal place to the machine's Start transition. During off-periods the machine is blocked; items wait in the queue and processing resumes when the next active window starts. Call AFTER both add_scheduled_availability and add_machine / add_breakdown. Args: schedule_name: Name passed to add_scheduled_availability (e.g., "DayShift") machine_name: Name passed to add_machine or add_breakdown (e.g., "Press") |
| connect_patternsC | Connect the output of one pattern to the input of another. Args: source_output_id: Output place ID from the source pattern target_input_id: Input place ID of the target pattern |
| get_network_statusA | Get current status of the Petri Net being built. Returns summary of places, transitions, arcs, and validation status. |
| add_placeA | Add a single place (position) to the Petri net. LOW-LEVEL TOOL — use only when no existing pattern (add_generator, add_machine, etc.) covers the required topology. For standard queueing and production constructs, prefer the high-level pattern tools. Layout: ALWAYS call find_free_position(near_element_id, direction) first to get collision-free coordinates near the element you will connect to, then pass the returned x and y here. Without explicit coordinates the auto-cursor places the element at the far right, colliding with existing elements on follow-up edits. Args: name: Human-readable place name tokens: Initial token count (marking) tag: Optional semantic tag (e.g. "throughput" for counter places) x: Canvas X coordinate — obtain from find_free_position y: Canvas Y coordinate — obtain from find_free_position |
| add_transitionA | Add a single transition (event) to the Petri net. LOW-LEVEL TOOL — use only when no existing pattern covers the required topology. For standard constructs, prefer high-level pattern tools. Layout: ALWAYS call find_free_position(near_element_id, direction) first to get collision-free coordinates, then pass the returned x and y here. Args: name: Human-readable transition name distribution: Time distribution ("exp", "norm", "unif", "det"), omit for immediate mean_delay: Mean delay time in time_unit standard_deviation: For norm/unif distributions (in time_unit) priority: Firing priority (higher = fires first in conflict) probability: Firing probability for stochastic conflict resolution (0.0-1.0) time_unit: Unit for time values — "s", "min", "h" x: Canvas X coordinate (see list_elements for existing coords) y: Canvas Y coordinate |
| add_arcA | Add an arc connecting a place to a transition or vice versa. LOW-LEVEL TOOL — use only when no existing pattern covers the required topology. Arcs enforce bipartite constraint: Place↔Transition only. Args: source_id: ID of the source node (place or transition) target_id: ID of the target node (place or transition) weight: Arc weight (tokens consumed/produced per firing) arc_type: Arc type — "normal", "inhibitor", or "read" |
| validate_netA | Validate the current Petri net topology. Checks bipartite constraint, dangling transitions (no input/output arcs), and duplicate IDs. Call this after building custom topology with add_place, add_transition, add_arc to catch errors before export. |
| modify_placeA | Modify an existing place in the Petri net. Only provided fields are updated — omitted fields stay unchanged. Use list_elements or get_network_status to discover place IDs first. Args: place_id: ID of the place to modify (e.g. "p1") name: New name (omit to keep current) tokens: New token count (omit to keep current) tag: New semantic tag (omit to keep current) |
| modify_transitionA | Modify an existing transition in the Petri net. Only provided fields are updated — omitted fields stay unchanged. Use list_elements or get_network_status to discover transition IDs first. Args: transition_id: ID of the transition to modify (e.g. "t3") name: New name (omit to keep current) distribution: New distribution ("exp", "norm", "unif", "det") — omit to keep current mean_delay: New mean delay in time_unit (omit to keep current) standard_deviation: New std deviation in time_unit (omit to keep current) priority: New firing priority — higher number fires first in conflict (omit to keep current) probability: New firing probability 0.0-1.0 (omit to keep current) time_unit: Unit for time values — "s", "min", "h" |
| modify_arcA | Modify an existing arc in the Petri net. Only provided fields are updated — omitted fields stay unchanged. Use list_elements to discover arc IDs first. Args: arc_id: ID of the arc to modify weight: New arc weight (omit to keep current) arc_type: New arc type — "normal", "inhibitor", or "read" (omit to keep current) |
| remove_placeA | Remove a place and all its connected arcs from the Petri net. WARNING: Destructive — the place and ALL arcs connected to it are deleted. Args: place_id: ID of the place to remove |
| remove_transitionA | Remove a transition and all its connected arcs from the Petri net. WARNING: Destructive — the transition and ALL arcs connected to it are deleted. Args: transition_id: ID of the transition to remove |
| remove_arcC | Remove a single arc from the Petri net. Args: arc_id: ID of the arc to remove |
| list_elementsA | List all places, transitions, and arcs with their current parameters. Use this before modify/remove operations to discover element IDs and current values. Returns detailed info for every element in the net. |
| set_positionsA | Set canvas coordinates for places and transitions by ID. Use after adding new elements or to clean up a cluttered layout. Typical workflow:
Elements not listed keep their current positions. Args: positions: List of {"id": "", "x": , "y": } Example: [{"id": "p1", "x": 100, "y": 200}, {"id": "t1", "x": 200, "y": 200}] |
| find_free_positionA | Find canvas coordinates for a new element near an existing one, avoiding overlaps. Call this BEFORE add_place or add_transition to get good x,y coordinates. Then pass those coordinates directly to add_place/add_transition. Recommended workflow when adding a place connected to an existing element:
Args: near_element_id: ID of the place or transition to position near (use list_elements to find IDs and current coordinates) direction: Where to place the new element relative to the anchor: "above" (same x, lower y) | "below" | "left" | "right" |
| find_elementsA | Search places and transitions by name substring. Use this to locate element IDs before calling find_free_position, set_positions, or move_elements_by. Much faster than list_elements when you only need a specific pattern's elements. Typical workflow for placing a new element near an existing one:
Args: name_pattern: Case-insensitive substring (e.g. "Generator", "Queue"). Empty string returns all elements of the requested type. element_type: "place", "transition", or omit for both. |
| get_network_boundsA | Return the spatial bounding box of all elements in the net. Use to understand the current layout extent before adding new elements or deciding where to place a new pattern relative to existing ones. Returns: min_x, max_x — horizontal extent min_y, max_y — vertical extent center_x, center_y — geometric center of all elements width, height — overall canvas span element_count — total places + transitions |
| move_elements_byA | Shift one or more elements by a relative offset. Use when the user says "move the generator 200px to the right" or "push the machine down by 100px", without needing to query current coordinates first. To move all elements of a named pattern, call find_elements first to collect the IDs, then pass them here. Args: element_ids: List of place/transition IDs to move dx: Horizontal shift in canvas units (positive = right) dy: Vertical shift in canvas units (positive = down) |
| auto_layoutA | Re-layout the entire net using topological left-to-right layering. Assigns x,y coordinates to every element based on the arc topology. Elements with no predecessors get layer 0; successors get later layers. Elements in the same layer are distributed symmetrically around center_y. Call this after building the complete net when the automatic cursor placement produced a cluttered or overlapping layout. Args: start_x: X coordinate for the first (leftmost) layer (default 100) center_y: Vertical center for all layers (default 200) x_step: Horizontal distance between layers in canvas units (default 150) y_step: Vertical distance between elements in the same layer (default 120) |
| add_terminatorD | Add terminator pattern. |
| add_bufferD | Add buffer pattern. |
| add_quality_checkC | Add quality_check pattern. |
| add_feedback_loopC | Add feedback_loop pattern. |
| add_threshold_activationC | Add threshold_activation pattern. |
| add_bounded_queueD | Add bounded_queue pattern. |
| add_batchC | Add batch pattern. |
| add_unbatchA | Unbatch (Split / Unpack) pattern. One incoming token (representing a batch or container) is expanded into
Structure: [P: Input] -(weight=1)-> [T: Split] -(weight=N)-> [P: Output] Use cases:
|
| add_breakdownD | Add breakdown pattern. |
| add_scheduled_availabilityD | Add scheduled_availability pattern. |
| add_reneging_queueD | Add reneging_queue pattern. |
| add_setup_machineD | Add setup_machine pattern. |
| add_server_vacationD | Add server_vacation pattern. |
| add_balking_queueD | Add balking_queue pattern. |
| add_timed_batchD | Add timed_batch pattern. |
| add_preemptive_machineD | Add preemptive_machine pattern. |
| add_retrial_queueC | Add retrial_queue pattern. |
| add_blocking_machineD | Add blocking_machine pattern. |
| add_finite_population_queueA | Closed queueing network: N entities recirculate through a c-server queue. Models M/M/c/∞/N systems where N customers alternate between "thinking"
(off-system rest) and waiting for service. After service completes the entity
rests for Structure: [IdlePool(N)] --[Request DET=0]--> [Queue] [Queue] + [FreeChannels(c)] --[Start DET=0]--> [InService] [InService] --[EndService]--> [Done] + FreeChannels returned [Done] --[Measure DET=0]--> [Resting] + [Counter(throughput)] [Resting] --[EndRest]--> [IdlePool] Token conservation: IdlePool + Queue + InService + Resting = N at all times. Counter accumulates as a monotone throughput counter (same convention as Terminator). Use cases:
When thinking_time=0 entities return immediately (pure circulation, no idle phase). |
| add_wip_controllerA | WIP (Work In Progress) limiter — Kanban / CONWIP entry gate. Limits the number of entities in a controlled section to at most Structure: [Input] + [WIPSlots(K)] --[Enter DET=0]--> [Output] After wiring the controlled section, call connect_wip_return to close the loop: each transition T that produces tokens into the section's exit place gets an extra output arc T -> [WIPSlots]. The entity is NOT consumed — it continues to flow through the exit place to its downstream consumer (terminator, next stage, etc.) while a WIP slot is recycled. Use cases:
|
| optimize_networkA | Optimize the Petri Net by removing redundant elements. Call this AFTER the network is fully built, BEFORE exporting. Removes intermediate Transfer transitions and simplifies the topology while preserving semantic behavior, markings, and timing. |
| export_pnmlA | Export the current Petri Net to PNML XML format. Returns the full PNML XML content as a string. Compatible with TINA, PetriObjModel, and CPN Tools. |
| run_simulationA | Run a discrete-event simulation of the current Petri Net. Executes the simulation for the given time and returns statistics: final markings, mean values, and observed min/max for all places and transitions, plus a Terminators (throughput) block sourced from places tagged "throughput". IMPORTANT: simulation_time MUST use the same conceptual unit as the transition delays you set when building the net. Pass time_unit explicitly — the response echoes the duration in s/min/h/d so you can verify the interpretation matches your intent. Args: simulation_time: Total simulation time in time_unit time_unit: "s" (seconds, default), "min", "h", or "d" (days) |
| get_simulation_resultsA | Get the results of the last simulation run. Returns formatted statistics from the most recent run_simulation call. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
| build_queueing_system | Build a Petri net model from a queueing system description. |
| build_production_line | Build a Petri net model for a production/manufacturing line. |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
Latest Blog Posts
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/petri-net-sim/pns-server'
If you have feedback or need assistance with the MCP directory API, please join our Discord server