Skip to main content
Glama
soobrosa

poietic-mcp

by soobrosa
README.md
# poietic-mcp

An [MCP](https://modelcontextprotocol.io) server that lets an agent read, edit,
validate and simulate [Poietic](https://www.poietic.org/) Stock and Flow
designs, alongside a human working in
[Poietic Playground](https://github.com/OpenPoiesis/poietic-playground).

The design file is the integration point. The agent edits a `.poietic` file
through this server by driving the
[`poietic` CLI](https://github.com/OpenPoiesis/poietic-tool); the human opens
or reloads the same file in the playground. Agent edits, human views and
direct-manipulates; both sides re-read.

## Install

```bash
# 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

```bash
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):

```json
{
  "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):

```json
{
  "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:

```bash
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](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

```bash
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](https://github.com/OpenPoiesis/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.

TDQS

A4.1/5.0

Scored across 18 tools

Disambiguation5/5

Each tool targets a distinct capability: schema discovery, design lifecycle, node/edge editing, validation, simulation, layout, and export/import. Even the export tools are clearly separated by output format, and the manual edge creation vs auto-wiring tools serve different workflow stages.

Naming Consistency4/5

All tools share the poietic_ prefix and mostly use imperative/snake_case names (get_design, add_node, set_attributes, export_svg). A few names are verb-only (validate, connect, run) and poietic_metamodel is a noun-only outlier, so the pattern is not perfectly uniform.

Tool Count4/5

Eighteen tools is slightly above the typical comfortable range, but each maps to a needed operation across model editing, simulation, and exchange. The count feels justified rather than bloated for a domain with both editing and simulation/export workflows.

Completeness5/5

The set covers schema discovery, design creation/loading/reading, node and edge CRUD-style operations, attribute updates, validation, undo/redo, simulation, layout, and multiple export/import paths. There are no obvious dead ends for a stock-and-flow modeling workflow.

Maintenance

ActivityMaintained
ResponsivenessNo issues