Skip to content

Repository files navigation

poietic-mcp

An MCP server that lets an agent read, edit, validate and simulate Poietic Stock and Flow designs, alongside a human working in Poietic Playground.

The design file is the integration point. The agent edits a .poietic file through this server by driving the poietic CLI; the human opens or reloads the same file in the playground. Agent edits, human views and direct-manipulates; both sides re-read.

Install

# 1. Build and install the poietic CLI (Swift 6.x, macOS 15+ or Linux)
git clone https://github.com/OpenPoiesis/poietic-tool
cd poietic-tool && git checkout v0.9 && ./install  # installs to ~/.swiftpm/bin/poietic

# 2. Build this server
git clone https://github.com/soobrosa/poietic-mcp
cd poietic-mcp && npm install && npm run build

Configure

The server works with any MCP client. The command to register is always:

node <absolute-path-to>/poietic-mcp/dist/index.js

with optional environment variables (see below). Environment:

  • POIETIC_MCP_WORKSPACE - allowlist root. Design paths and export outputs must live inside it. Defaults to the server's working directory. Set it to a dedicated designs folder (e.g. ~/Documents/poietic-designs) for a tight fence, or your home directory to work with designs anywhere.
  • POIETIC_BIN - path to the poietic binary. Defaults to ~/.swiftpm/bin/poietic.
  • POIETIC_DESIGN - optional default design path, used when no current design is set.

Claude Code

claude mcp add --scope user poietic \
  --env POIETIC_MCP_WORKSPACE=$HOME \
  --env POIETIC_BIN=$HOME/.swiftpm/bin/poietic \
  -- /opt/homebrew/bin/node /absolute/path/to/poietic-mcp/dist/index.js

Verify with claude mcp list (should show poietic: ... - ✔ Connected) or /mcp inside a session. --scope user makes it available in all projects; use --scope project to register it only for one repository (writes to that repo's .mcp.json).

Claude Desktop

Add the server to ~/Library/Application Support/Claude/claude_desktop_config.json (create the mcpServers key if absent), then fully quit and reopen the app (Cmd+Q; MCP servers only start at launch):

{
  "mcpServers": {
    "poietic": {
      "command": "/opt/homebrew/bin/node",
      "args": ["/absolute/path/to/poietic-mcp/dist/index.js"],
      "env": {
        "POIETIC_MCP_WORKSPACE": "/Users/you",
        "POIETIC_BIN": "/Users/you/.swiftpm/bin/poietic"
      }
    }
  }
}

The poietic tools appear under the tools icon in a chat.

Factory Droid / other clients

Register the same command in the client's MCP settings (e.g. .mcp.json in the workspace for Droid, project config for other clients):

{
  "mcpServers": {
    "poietic": {
      "command": "node",
      "args": ["/absolute/path/to/poietic-mcp/dist/index.js"],
      "env": {
        "POIETIC_MCP_WORKSPACE": "/Users/you/Documents/poietic-designs",
        "POIETIC_BIN": "/Users/you/.swiftpm/bin/poietic"
      }
    }
  }
}

Verifying the setup

From a terminal, drive the server directly like an MCP client would:

node src/test/smoke.mjs   # registers, builds a tiny model, simulates

Using it

You do not invoke tools by name; you talk, and the agent picks the poietic_* tools as the conversation turns to building models.

A typical build request:

Create a new Poietic design called fishing.poietic in ~/Documents/poietic-designs and model the fishing economy from Thinking in Systems: a fleet (capital) that grows 5% per year, fish that regenerate as a function of fish density, harvest yield falling as fish get scarce, price rising as fish get scarce. Validate and simulate 200 years.

A typical inspect-and-modify request:

Open Capital.poietic in ~/Downloads/_Converted/ThinkingInSystems, show me the structure, then double the initial resource and re-run the simulation.

Working alongside Poietic Playground: open the same design file in the playground to see and edit the agent's work by hand. After saving in the playground, the agent's next write is refused with a fingerprint conflict; tell it to re-read the design (poietic_get_design) and continue. See PLAYGROUND.md for the handoff protocol.

The guided recipe: ask the agent to use the build_stock_and_flow_model prompt for a description of a system. It encodes node roles (Stock vs FlowRate vs Auxiliary), Flow vs Parameter edge semantics, formula syntax, and the validate-then-run ordering - including two lessons learned the hard way:

  • Node names can contain spaces. In formulas, wrap those names in curly braces, for example {account balance} * {interest rate}. Name matching is case-insensitive, and spaces and underscores normalize to the same name.
  • poietic_auto_parameters does not wire Parameter edges into or out of GraphicalFunction nodes. After auto-parameters, validate; for each issue, connect the missing edge explicitly and re-validate.
  • A stated growth-rate goal is usually a net rate. An investment goal of g% of the stock with d% depreciation yields net growth of (g - d)%; setting g = d freezes the stock. Gross goal = depreciation + desired growth.

Tips:

  • Mention the design's folder so the agent stays inside the workspace fence.
  • Ask for poietic_validate before simulating; the agent will usually do it unprompted, but a broken model fails in poietic_run with the same issues.
  • Ask for results as summaries, not raw dumps - poietic_run already returns a per-variable summary with capped sample rows, and can write the full CSV to a file if you give it an outputPath.

Tools

Session: poietic_use_design binds the current design (and snapshots a content-hash fingerprint as the session baseline); every other tool takes an optional design override.

Tool Purpose
poietic_metamodel(type?) Metamodel as markdown (cached). Schema discovery.
poietic_get_design() Normalized design JSON: {nodes[], edges[], unstructured[]}.
poietic_validate() {ok, issues[]} with per-object formula/parameter errors.
poietic_new_design({path, import?}) Create an empty design; becomes current.
poietic_add_node({type, attributes}) Create a node; returns {objectId, planeId}.
poietic_connect({type, origin, target, attributes?}) Create an edge, optionally with v0.9 attributes; returns {edgeId}.
poietic_set_attributes({ref, attributes}) Set attributes; one undo entry (single transaction on poietic-tool v0.8+).
poietic_unset_attributes({ref, attributes}) Remove stored attribute values in one transaction (v0.9+).
poietic_remove({ref}) Remove a node or edge (cascades edges).
poietic_auto_parameters() Wire required parameter edges, drop unused ones.
poietic_undo() / poietic_redo() One tool call = one transaction.
poietic_arrange({mode, refs?}) circle layout or align modes.
poietic_run({...}) Simulate with startTime, finalTime, timeStep, euler/rk4, and display/normalized names; summarize results or write full CSV.
poietic_export_svg({outputPath}) SVG diagram export.
poietic_write_dot({outputPath?}) Graphviz DOT (stdout if no path).
poietic_export_plane / poietic_import_plane Plane exchange between design files.

Resources: poietic://metamodel, poietic://builtins (formula operators, functions and variables; poietic-tool v0.8+), poietic://design/current, poietic://design/current/diagram.svg.

For a v0.9 Simulation settings object, use start_time, time_step, and final_time (replacing initial_time, time_delta, and end_time). solver_type accepts euler or the fixed rk4 solver. flow_scaling accepts outflow_first or inflow_first; set it when competing flows may exceed available stock. The steps run option is deprecated, so prefer finalTime.

Prompt: build_stock_and_flow_model - encodes node roles, Flow vs Parameter edge semantics, formula syntax, and the validate-then-run ordering.

Safety model

  • Argv only. The CLI is spawned with an argv array; formulas containing *, spaces or parentheses are never interpreted by a shell.
  • Per-design mutex. Every CLI invocation is a whole-file read-modify-write; concurrent calls are serialized per design.
  • Fingerprint guard. After the first bind, any external change to the design file (e.g. the human saving in the playground) blocks writes with a clear error until the agent re-reads the design. Conflicts are detected, never merged.
  • Path allowlist. Designs and outputs must be inside POIETIC_MCP_WORKSPACE.
  • Verbatim errors. Non-zero CLI exits surface the CLI's stderr unchanged.
  • No silent nulls. A create whose output cannot be parsed for the new object ID is a hard error.

Development

npm run build        # tsc
npm test             # parser fixtures + integration (needs the poietic binary)
node src/test/smoke.mjs   # drives the built server over stdio
node src/test/from-description.mjs   # builds a model from prose only

fixtures/raw/ holds recorded stdout/stderr from the real CLI (see fixtures/FINDINGS.md for observed behavior and deviations from the docs); fixtures/capture.sh reproduces them. src/test/capital.test.mjs rebuilds the Thinking in Systems Capital model through the MCP tools and asserts the rebuilt simulation reproduces the committed original variable by variable.

Troubleshooting

  • poietic binary not found - build and install poietic-tool, or point POIETIC_BIN at the binary.
  • Path is outside the allowed workspace - the design path is not under POIETIC_MCP_WORKSPACE; move the file, or update the server's env in the client config (restart the client afterwards).
  • The design changed outside this session - the file was saved externally (playground, another agent). Re-read it with poietic_get_design, then retry the write.
  • Validation issues about graphical functions - run poietic_auto_parameters, then connect the missing GraphicalFunction edges explicitly (see the from-prose lessons under "Using it").
  • Claude Desktop shows no poietic tools - the app loads MCP servers only at launch; quit fully (Cmd+Q) and reopen. Logs: ~/Library/Logs/Claude/mcp*.log.

Known limitations

  • No live playground drive. The human reopens the file; the playground has no reload-on-change.
  • Concurrent editing is detected, not merged. The fingerprint guard refuses the write; re-read and re-apply.
  • Multi-step agent edits are not atomic. Each tool call is one transaction; one undo reverses one call. (poietic_set_attributes with several attributes is a single transaction on poietic-tool v0.8+; older builds create one entry per attribute.)
  • Duplicate object names resolve arbitrarily upstream, so name-based references are only safe when uniqueness is known. Prefer numeric IDs.

About

MCP connector for Poietic Stock and Flow designs, driven by the poietic CLI

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages