pmx-canvas 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +124 -0
- package/Readme.md +2 -2
- package/dist/canvas/global.css +260 -0
- package/dist/canvas/index.js +76 -76
- package/dist/json-render/index.js +2 -2
- package/dist/types/client/canvas/IntentLayer.d.ts +1 -0
- package/dist/types/client/state/intent-bridge.d.ts +10 -0
- package/dist/types/client/state/intent-store.d.ts +25 -0
- package/dist/types/json-render/server.d.ts +1 -1
- package/dist/types/server/ax-state-manager.d.ts +11 -0
- package/dist/types/server/ax-state.d.ts +2 -0
- package/dist/types/server/canvas-db.d.ts +13 -0
- package/dist/types/server/canvas-state.d.ts +5 -0
- package/dist/types/server/index.d.ts +34 -4
- package/dist/types/server/intent-registry.d.ts +45 -0
- package/dist/types/server/operations/ops/intent.d.ts +2 -0
- package/dist/types/shared/ax-intent.d.ts +58 -0
- package/docs/ax-host-adapter-contract.md +19 -1
- package/docs/http-api.md +4 -0
- package/docs/mcp.md +22 -3
- package/docs/screenshot.png +0 -0
- package/package.json +1 -1
- package/skills/pmx-canvas/SKILL.md +197 -1283
- package/skills/pmx-canvas/evals/evals.json +199 -0
- package/skills/pmx-canvas/references/ax-html-control-surface.md +93 -0
- package/skills/pmx-canvas/references/full-reference.md +1441 -0
- package/skills/pmx-canvas/references/github-copilot-app-adapter.md +23 -7
- package/src/cli/index.ts +21 -4
- package/src/client/canvas/CanvasNode.tsx +13 -13
- package/src/client/canvas/CanvasViewport.tsx +2 -0
- package/src/client/canvas/ContextMenu.tsx +25 -19
- package/src/client/canvas/IntentLayer.tsx +278 -0
- package/src/client/nodes/ExtAppFrame.tsx +31 -22
- package/src/client/state/intent-bridge.ts +31 -0
- package/src/client/state/intent-store.ts +107 -0
- package/src/client/state/sse-bridge.ts +31 -0
- package/src/client/theme/global.css +260 -0
- package/src/json-render/charts/components.tsx +18 -4
- package/src/json-render/renderer/index.tsx +11 -2
- package/src/json-render/server.ts +1 -1
- package/src/server/ax-context.ts +8 -1
- package/src/server/ax-state-manager.ts +18 -0
- package/src/server/ax-state.ts +8 -0
- package/src/server/canvas-db.ts +35 -0
- package/src/server/canvas-state.ts +8 -0
- package/src/server/index.ts +240 -158
- package/src/server/intent-registry.ts +324 -0
- package/src/server/operations/composites.ts +11 -0
- package/src/server/operations/index.ts +2 -0
- package/src/server/operations/ops/edges.ts +1 -0
- package/src/server/operations/ops/groups.ts +3 -0
- package/src/server/operations/ops/intent.ts +132 -0
- package/src/server/operations/ops/json-render.ts +3 -0
- package/src/server/operations/ops/nodes.ts +3 -0
- package/src/server/operations/registry.ts +68 -3
- package/src/server/server.ts +40 -12
- package/src/shared/ax-intent.ts +64 -0
- package/src/shared/surface.ts +5 -1
|
@@ -10,1301 +10,215 @@ description: >
|
|
|
10
10
|
working memory — pin nodes to curate context, read spatial arrangement to understand intent.
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
# PMX Canvas
|
|
14
|
-
|
|
15
|
-
PMX Canvas is a spatial
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
the
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
When the human asks you to use a data source or tool you have not used
|
|
45
|
-
before — an MCP server, an MCP app, a CLI, a script, an arbitrary file
|
|
46
|
-
tree:
|
|
47
|
-
|
|
48
|
-
1. List the tools / commands available; sample one or two outputs to see
|
|
49
|
-
the actual shape.
|
|
50
|
-
2. Decide which canvas node type best matches each output:
|
|
51
|
-
- Long-form / narrative results → `markdown`
|
|
52
|
-
- Structured records, tables, dashboards → `json-render` or `graph`
|
|
53
|
-
- Rich reusable communication artifacts → `html-primitive`
|
|
54
|
-
- Interactive tool surfaces with their own UI → `mcp-app` (open with
|
|
55
|
-
`canvas_open_mcp_app`)
|
|
56
|
-
- Local source files → `file` (live-watched)
|
|
57
|
-
- URLs that need cached fetches → `webpage`
|
|
58
|
-
- Stream of state events → `status` or `ledger`
|
|
59
|
-
3. Propose the mapping to the human before bulk-creating nodes; let them
|
|
60
|
-
confirm or adjust before you commit a layout.
|
|
61
|
-
|
|
62
|
-
## Starting the Canvas
|
|
63
|
-
|
|
64
|
-
If this skill is installed before the `pmx-canvas` command exists, install the project first. See
|
|
65
|
-
`references/installing-pmx-canvas.md` for local development, npm/global install, and MCP config
|
|
66
|
-
options.
|
|
67
|
-
|
|
68
|
-
## Adapter References
|
|
69
|
-
|
|
70
|
-
PMX Canvas core is host-agnostic. When a host-specific adapter is available, read the matching
|
|
71
|
-
reference before using adapter-native features:
|
|
72
|
-
|
|
73
|
-
- `references/github-copilot-app-adapter.md` — GitHub Copilot app project extension, native canvas
|
|
74
|
-
panel, AX context injection, and live-test checklist.
|
|
75
|
-
- `references/codex-app-adapter.md` — Codex app native Browser + MCP adapter, AX context reading,
|
|
76
|
-
focus labeling, and live-test checklist.
|
|
77
|
-
|
|
78
|
-
Open the canvas first — always:
|
|
79
|
-
The canvas is the shared human↔agent surface. **Before you create or mutate any nodes, make the
|
|
80
|
-
workbench visible.** Do **not** assume the host opened it for you — some hosts (e.g. the Codex app)
|
|
81
|
-
do not open it on their own. Take the action to open it yourself, whatever the host:
|
|
82
|
-
- **Native adapter/panel available** (the GitHub Copilot app `pmx-canvas` canvas extension, or the
|
|
83
|
-
Codex in-app Browser): open/focus that panel to the server's `/workbench` route.
|
|
84
|
-
- **Any other browser** (Chrome, Safari, Arc, Edge, …) **or a generic/CLI agent** with no native
|
|
85
|
-
panel: open the server's `/workbench` URL in a browser.
|
|
86
|
-
|
|
87
|
-
Then reuse that **single** surface for the rest of the session — do **not** open a second panel to
|
|
88
|
-
the same workbench (it wastes space and confuses which surface is authoritative). If you genuinely
|
|
89
|
-
cannot open any browser (headless/CI), say so and proceed, but still print the `/workbench` URL so a
|
|
90
|
-
human can open it.
|
|
91
|
-
- External URLs in `mcp-app` nodes show the "Unverified domain" interstitial by design. Only
|
|
92
|
-
same-origin `/api/canvas/frame-documents/<id>` URLs are auto-trusted. For external tools, use a
|
|
93
|
-
bundled `web-artifact`, same-origin frame document, or set `data.trustedDomain: true` only when the
|
|
94
|
-
user accepts the risk.
|
|
95
|
-
|
|
96
|
-
The canvas auto-starts on first MCP tool call when running in MCP mode (`pmx-canvas --mcp`).
|
|
97
|
-
For manual start:
|
|
98
|
-
|
|
99
|
-
```bash
|
|
100
|
-
pmx-canvas # Start and open browser (port 4313)
|
|
101
|
-
pmx-canvas --no-open # Start without opening browser (for agents)
|
|
102
|
-
pmx-canvas --port=8080 # Custom port
|
|
103
|
-
pmx-canvas --demo # Start with sample content
|
|
104
|
-
pmx-canvas --theme=light # Light theme
|
|
105
|
-
pmx-canvas --version # Print installed version and exit
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
`--theme` accepts `dark` (default), `light`, or `high-contrast`. Same value can be set via
|
|
109
|
-
the `PMX_CANVAS_THEME` environment variable, or toggled live in the browser toolbar.
|
|
110
|
-
|
|
111
|
-
Start the canvas once per session, then reuse it. Use `--no-open` when running as an agent — the
|
|
112
|
-
human can open the browser URL themselves.
|
|
113
|
-
|
|
114
|
-
### Daemon mode (long-running background server)
|
|
115
|
-
|
|
116
|
-
When you need the canvas to outlive the current shell or agent session — e.g. so a follow-up
|
|
117
|
-
agent run can attach to the same state — start it as a daemon:
|
|
13
|
+
# PMX Canvas
|
|
14
|
+
|
|
15
|
+
PMX Canvas is a server-authoritative spatial workbench controlled through MCP, HTTP, or the CLI.
|
|
16
|
+
Humans curate agent context by pinning nodes; agents read that curation through
|
|
17
|
+
`canvas://pinned-context`. State survives browser refresh.
|
|
18
|
+
|
|
19
|
+
## Required Operating Sequence
|
|
20
|
+
|
|
21
|
+
1. **Open or focus the workbench before mutating.** Reuse one visible canvas surface for the
|
|
22
|
+
session.
|
|
23
|
+
2. **Verify workspace identity.** Read `GET /health` or `pmx-canvas serve status`; the returned
|
|
24
|
+
`workspace` must equal the intended absolute workspace root. A healthy listener on port 4313
|
|
25
|
+
may belong to another project.
|
|
26
|
+
3. **Read before write.** Search with `canvas_query { action: "search", query }` before creating
|
|
27
|
+
nodes. Read the full layout only when necessary.
|
|
28
|
+
4. **Snapshot before destructive changes.** Use `canvas_snapshot` before clear, restore, or a major
|
|
29
|
+
reorganization.
|
|
30
|
+
5. **Signal substantial spatial changes.** Use `canvas_intent { action: "signal", ... }` before a
|
|
31
|
+
visible create, move, connect, remove, or edit when human steering would be useful.
|
|
32
|
+
6. **Mutate through current composites.** Prefer the 15 composite MCP tools below.
|
|
33
|
+
7. **Arrange and validate.** After batch changes, use `canvas_view { action: "arrange" }` when
|
|
34
|
+
appropriate and always finish with `canvas_query { action: "validate" }`.
|
|
35
|
+
8. **Verify context pins.** Pin with `canvas_pin_nodes` or the browser's **Pin as context**, then
|
|
36
|
+
read `canvas://pinned-context`.
|
|
37
|
+
9. **Clean up temporary nodes.** Remove retry/test fixtures and restore the baseline snapshot when
|
|
38
|
+
the task requires leaving the board unchanged.
|
|
39
|
+
|
|
40
|
+
## Workspace Safety
|
|
41
|
+
|
|
42
|
+
Before any create, update, remove, clear, restore, or arrange:
|
|
118
43
|
|
|
119
44
|
```bash
|
|
120
|
-
|
|
121
|
-
pmx-canvas serve status
|
|
122
|
-
pmx-canvas serve stop # Stop the daemon for this port
|
|
45
|
+
curl -sS http://localhost:4313/health
|
|
46
|
+
pmx-canvas serve status
|
|
123
47
|
```
|
|
124
48
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
49
|
+
Both surfaces report `workspace`. It must match the intended workspace root.
|
|
50
|
+
|
|
51
|
+
- If `responsive: true` but `pidRunning: false`, treat the listener as potentially stale.
|
|
52
|
+
- On mismatch, do not mutate. Start the intended workspace on an explicit free port:
|
|
53
|
+
`pmx-canvas serve --daemon --no-open --port=<free-port>`.
|
|
54
|
+
- Target that port and re-check `/health`.
|
|
55
|
+
- `PMX_CANVAS_PORT` is the agent CLI target; the server's startup port is controlled by `--port`
|
|
56
|
+
or `PMX_WEB_CANVAS_PORT`.
|
|
57
|
+
|
|
58
|
+
## Choose the Smallest Useful Node Type
|
|
59
|
+
|
|
60
|
+
| Need | Node/tool |
|
|
61
|
+
|------|-----------|
|
|
62
|
+
| Narrative, note, explanation | `markdown` via `canvas_node` |
|
|
63
|
+
| Progress or current state | `status` via `canvas_node` |
|
|
64
|
+
| Persistent context cards | `context` via `canvas_node` |
|
|
65
|
+
| Event/check stream | `ledger` or `trace` via `canvas_node` |
|
|
66
|
+
| Local source with live updates | `file` via `canvas_node` |
|
|
67
|
+
| Image | `image` via `canvas_node` |
|
|
68
|
+
| Cached URL content | `webpage` via `canvas_node` |
|
|
69
|
+
| Structured UI | `json-render` via `canvas_render` |
|
|
70
|
+
| Chart | `graph` via `canvas_render` |
|
|
71
|
+
| Generated communication surface | HTML primitive via `canvas_node` |
|
|
72
|
+
| Self-contained HTML/JS | `html` via `canvas_node` |
|
|
73
|
+
| Hosted interactive MCP app | `canvas_app { action: "open-mcp-app" }` |
|
|
74
|
+
| Excalidraw diagram | `canvas_app { action: "diagram" }` |
|
|
75
|
+
| Bundled React artifact | `canvas_app { action: "build-artifact" }` |
|
|
76
|
+
|
|
77
|
+
Use the lightest tier that communicates the result. Do not build a web artifact when markdown,
|
|
78
|
+
json-render, a graph, or an HTML primitive is sufficient.
|
|
79
|
+
|
|
80
|
+
## Current MCP Composites
|
|
81
|
+
|
|
82
|
+
| Composite | Actions |
|
|
83
|
+
|-----------|---------|
|
|
84
|
+
| `canvas_node` | `add`, `get`, `update`, `remove` |
|
|
85
|
+
| `canvas_render` | `describe-schema`, `validate`, `add-json-render`, `stream-json-render`, `add-graph` |
|
|
86
|
+
| `canvas_edge` | `add`, `remove` |
|
|
87
|
+
| `canvas_group` | `create`, `add`, `ungroup` |
|
|
88
|
+
| `canvas_history` | `undo`, `redo` |
|
|
89
|
+
| `canvas_view` | `arrange`, `focus`, `fit`, `clear`, `remove-annotation` |
|
|
90
|
+
| `canvas_query` | `search`, `layout`, `validate` |
|
|
91
|
+
| `canvas_webview` | `status`, `start`, `stop`, `resize`, `evaluate` |
|
|
92
|
+
| `canvas_app` | `open-mcp-app`, `diagram`, `build-artifact` |
|
|
93
|
+
| `canvas_ax_state` | `get`, `set-focus`, `set-policy`, `report-capability` |
|
|
94
|
+
| `canvas_ax_work` | `add`, `update`, `annotate` |
|
|
95
|
+
| `canvas_ax_gate` | `request`, `resolve`, `await` with `approval`, `elicitation`, or `mode` |
|
|
96
|
+
| `canvas_ax_timeline` | `read`, `record-event`, `add-evidence`, `send-steering` |
|
|
97
|
+
| `canvas_ax_delivery` | `claim`, `mark` |
|
|
98
|
+
| `canvas_intent` | `signal`, `update`, `clear` |
|
|
99
|
+
|
|
100
|
+
Important routing:
|
|
101
|
+
|
|
102
|
+
- Basic nodes: `canvas_node { action: "add", type, ... }`
|
|
103
|
+
- HTML: `canvas_node { action: "add", type: "html", html }`
|
|
104
|
+
- HTML primitive: `canvas_node { action: "add", type: "html", primitive, data }`
|
|
105
|
+
- Graph: `canvas_render { action: "add-graph", ... }`
|
|
106
|
+
- JSON render: `canvas_render { action: "add-json-render", ... }`
|
|
107
|
+
- MCP app: `canvas_app { action: "open-mcp-app", ... }`
|
|
108
|
+
- Excalidraw: `canvas_app { action: "diagram", ... }`
|
|
109
|
+
- Web artifact: `canvas_app { action: "build-artifact", ... }`
|
|
110
|
+
|
|
111
|
+
Legacy single-purpose tools remain during the v0.2 compatibility window but are deprecated and
|
|
112
|
+
scheduled for removal in v0.3. Standalones that intentionally remain include `canvas_batch`,
|
|
113
|
+
`canvas_pin_nodes`, `canvas_screenshot`, snapshot tools, `canvas_ax_interaction`,
|
|
114
|
+
`canvas_ingest_activity`, and `canvas_invoke_command`.
|
|
115
|
+
|
|
116
|
+
## Spatial Rules
|
|
117
|
+
|
|
118
|
+
- Treat proximity as relatedness and top-left to bottom-right as reading order.
|
|
119
|
+
- Search before adding to avoid duplicate nodes.
|
|
120
|
+
- Extend the current board in place; do not evict prior nodes to add new material.
|
|
121
|
+
- Use groups only when the frame communicates meaningful containment.
|
|
122
|
+
- Keep related nodes 40–80 px apart and separate unrelated clusters by roughly 150–250 px.
|
|
123
|
+
- Use directed edges for actual relationships, not decoration.
|
|
124
|
+
- Edge types: `flow`, `depends-on`, `relation`, `references`.
|
|
125
|
+
- After manual or batch layout changes, run `canvas_query { action: "validate" }`.
|
|
126
|
+
|
|
127
|
+
## Context Pins
|
|
128
|
+
|
|
129
|
+
Context pins are the primary human-to-agent bridge:
|
|
130
|
+
|
|
131
|
+
1. Human pins nodes in the browser using **Pin as context**.
|
|
132
|
+
2. Agent reads `canvas://pinned-context`.
|
|
133
|
+
3. The resource includes pinned nodes and nearby unpinned neighbors.
|
|
134
|
+
|
|
135
|
+
Do not confuse context pinning with **Lock position**, which only excludes a node from auto-arrange.
|
|
136
|
+
Every node type, including `status`, can be removed through `canvas_node { action: "remove" }`, the
|
|
137
|
+
title-bar × control, or the **Close** context-menu action.
|
|
129
138
|
|
|
130
139
|
## Browser Workflows
|
|
131
140
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
-
|
|
136
|
-
-
|
|
137
|
-
-
|
|
138
|
-
|
|
139
|
-
-
|
|
140
|
-
|
|
141
|
-
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
pmx-canvas
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
pmx-canvas ax steer "focus on the failing test first"
|
|
192
|
-
pmx-canvas ax timeline --limit 50
|
|
193
|
-
pmx-canvas snapshot save --name "before-refactor"
|
|
194
|
-
pmx-canvas code-graph
|
|
195
|
-
pmx-canvas spatial
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
### CLI command groups
|
|
199
|
-
|
|
200
|
-
- `node add|list|get|update|remove` — manage nodes
|
|
201
|
-
- `node schema` — inspect running-server create schemas and canonical examples, with `--summary`, `--field`, and `--component` filters
|
|
202
|
-
- `graph add` — convenience alias for graph nodes; `node add --type graph` remains the canonical form
|
|
203
|
-
- Graph CLI fields accept both kebab-case flags and camelCase schema names, e.g. `--graph-type`/`--graphType`, `--x-key`/`--xKey`, and `--bar-color`/`--barColor`.
|
|
204
|
-
- Graph CLI height flags are split: use `--node-height`/`--nodeHeight` for the
|
|
205
|
-
canvas frame and `--chart-height` for the chart content. CLI `--height`
|
|
206
|
-
remains a frame-height compatibility alias.
|
|
207
|
-
- `edge add|list|remove` — manage edges
|
|
208
|
-
- Search-based edge selectors must be specific enough to resolve exactly one node. Queries like
|
|
209
|
-
`"DVT O3"` can be ambiguous; prefer the full visible title such as `"DVT O3 — GitOps"`.
|
|
210
|
-
- `search`, `layout`, `status`, `arrange`, `focus` — inspect and navigate the canvas. Prefer
|
|
211
|
-
`focus --no-pan` when you only need to select/raise a node without hijacking the human's camera.
|
|
212
|
-
- `ax status|context|focus` — inspect the host-agnostic AX layer; `ax context`
|
|
213
|
-
combines pinned context and AX focus for adapter prompt injection.
|
|
214
|
-
- `ax event add`, `ax steer`, `ax evidence add`, `ax timeline` — the AX timeline
|
|
215
|
-
(agent-events, steering messages, evidence). Persisted for diagnostics,
|
|
216
|
-
retention-bounded, and excluded from snapshots.
|
|
217
|
-
- `ax work add|update|list`, `ax approval request|resolve|list`,
|
|
218
|
-
`ax review add|list` — canvas-bound AX state (work items, approval gates,
|
|
219
|
-
review annotations) that rides snapshots and restore and is cleared by `clear`.
|
|
220
|
-
- `ax host report|status` — report/read the host/session capability (own partition).
|
|
221
|
-
- `ax command list|invoke`, `ax policy get|set` — list/invoke registry commands
|
|
222
|
-
(`pmx.plan`, `pmx.execute`, `pmx.promote-context`, `pmx.summarize`, `pmx.review`)
|
|
223
|
-
and read/patch the canvas-bound tool/prompt policy.
|
|
224
|
-
- `copilot install-extension [--dry-run] [--yes]` — install the bundled GitHub
|
|
225
|
-
Copilot adapter into a repo; the core stays host-agnostic.
|
|
226
|
-
- `fit [id ...]` — set the server viewport to fit the whole canvas or selected nodes before screenshots or whole-board review
|
|
227
|
-
- `screenshot --output <path>` — top-level shortcut for `webview screenshot`; supports `--format png|jpeg|webp` and `--quality`
|
|
228
|
-
- `json-render --schema|--examples` — inspect the json-render component catalog with `--component`/`--field` filters; same data as `node schema --type json-render` in a more direct shape
|
|
229
|
-
- `--strict-size` (alias `--scroll-overflow`) on `node add`/`node update` — keep explicit width/height fixed and scroll overflowing content instead of letting the renderer auto-fit. Useful for long markdown, dense webpages, and dashboards that should fit a tile-sized frame.
|
|
230
|
-
- `--show-legend false` / `--show-labels false` on `node add --type graph` and `graph add` — hide chart legends and pie slice labels for compact graph nodes in tile-style boards.
|
|
231
|
-
- `open` — open the current workbench in the browser
|
|
232
|
-
- `pin --list|--clear|<ids...>` — manage context pins
|
|
233
|
-
- `undo`, `redo`, `history` — time travel
|
|
234
|
-
- `snapshot save|list|restore|delete` — manage snapshots
|
|
235
|
-
- `group create|add|remove` — manage groups
|
|
236
|
-
- `clear --yes` — destructive clear with explicit confirmation
|
|
237
|
-
- `validate spec` — validate json-render specs and graph payloads without creating nodes
|
|
238
|
-
- `web-artifact build` — build bundled React/Tailwind HTML artifacts; use `--deps` for extra packages and `--include-logs` only when raw logs are useful
|
|
239
|
-
- `external-app add --kind excalidraw` — create the hosted Excalidraw preset; response includes `id` and `nodeId` aliases for the same canvas node
|
|
240
|
-
- `serve status|stop` — inspect and stop daemonized servers started with `serve --daemon`
|
|
241
|
-
- `code-graph`, `spatial` — analysis commands
|
|
242
|
-
|
|
243
|
-
Current caveat:
|
|
244
|
-
- `mcp-app` grouping is not fully uniform yet. Web artifact app nodes have grouped reliably, but
|
|
245
|
-
Excalidraw app nodes have shown inconsistent `group add` behavior and weaker rediscoverability
|
|
246
|
-
through search later in the session. When you plan to curate an app-heavy comparison area,
|
|
247
|
-
capture node IDs immediately after creation and verify membership with `node get --summary`,
|
|
248
|
-
`layout --summary`, or the browser selection state instead of relying on search alone.
|
|
249
|
-
- App-like nodes persist as `type: "mcp-app"` internally but serialized results include `kind`:
|
|
250
|
-
`web-artifact`, `external-app`, or `mcp-app`. Prefer `node list --type web-artifact` or
|
|
251
|
-
`node list --type external-app` when you need the operational subtype.
|
|
252
|
-
- Generic `pmx-canvas node add --type mcp-app` is intentionally not supported because app nodes
|
|
253
|
-
need app/session metadata. Use `pmx-canvas web-artifact build` for bundled React artifacts or
|
|
254
|
-
`pmx-canvas external-app add --kind excalidraw` for the Excalidraw preset.
|
|
255
|
-
- For local `image` nodes on macOS, iCloud/OneDrive cloud-only placeholder files are rejected with
|
|
256
|
-
a download-first hint. Download the image locally before adding it to the canvas.
|
|
257
|
-
|
|
258
|
-
The CLI targets `http://localhost:4313` by default. Override with `PMX_CANVAS_URL` or
|
|
259
|
-
`PMX_CANVAS_PORT` when the canvas is running elsewhere.
|
|
260
|
-
|
|
261
|
-
## Core Concepts
|
|
262
|
-
|
|
263
|
-
### Node Types
|
|
264
|
-
|
|
265
|
-
| Type | Purpose | When to use |
|
|
266
|
-
|------|---------|-------------|
|
|
267
|
-
| `markdown` | Rich formatted content | Explanations, documentation, notes, findings |
|
|
268
|
-
| `status` | Compact color-coded indicator | Progress tracking, build/test results, task state |
|
|
269
|
-
| `file` | Live file viewer (auto-watches) | Show source code with live updates on file change |
|
|
270
|
-
| `image` | Image display | Screenshots, diagrams, charts |
|
|
271
|
-
| `context` | Context card | Key context the human should see |
|
|
272
|
-
| `ledger` | Log/ledger viewer | Structured log data, audit trails |
|
|
273
|
-
| `trace` | Trace/timeline viewer | Execution traces, timelines |
|
|
274
|
-
| `mcp-app` | Hosted app/embed frame | Tool-backed MCP apps or external app content; not generic CLI-created notes |
|
|
275
|
-
| `json-render` | Native structured UI panel | Dashboards, forms, tables, interactive layouts from json-render specs |
|
|
276
|
-
| `graph` | Native chart panel | Line, bar, pie, area, scatter, radar, stacked-bar, composed, plus Tufte primitives (sparkline, dot-plot, bullet, slopegraph) rendered inside the canvas |
|
|
277
|
-
| `html` | Sandboxed HTML+JS document | Self-contained HTML with optional inline `<script>` and CDN imports rendered in a sandbox-restricted iframe; canvas theme tokens are auto-injected |
|
|
278
|
-
| `group` | Spatial container/frame | Visually group related nodes together |
|
|
279
|
-
| `prompt` | Prompt thread root | Canvas-native prompt entry points for agent conversations. **Internal type — surfaces in `canvas://layout` for thread rendering but is not created via the public `canvas_node { action: "add" }` API. Don't try to add one directly.** |
|
|
280
|
-
| `response` | Prompt reply / streamed answer | Agent responses linked to prompt threads. **Same internal-only restriction as `prompt`.** |
|
|
281
|
-
|
|
282
|
-
### Edge Types
|
|
283
|
-
|
|
284
|
-
| Type | Purpose | Example label |
|
|
285
|
-
|------|---------|---------------|
|
|
286
|
-
| `flow` | Sequential/directional | "then", "calls", "triggers" |
|
|
287
|
-
| `depends-on` | Dependency | "requires", "blocks" |
|
|
288
|
-
| `relation` | General relationship | "related to", "similar to" |
|
|
289
|
-
| `references` | Cross-reference | "see also", "documented in" |
|
|
290
|
-
|
|
291
|
-
Edges support `style` (solid/dashed/dotted) and `animated` flag. Always use descriptive labels.
|
|
292
|
-
|
|
293
|
-
**Edge direction convention:** `from` is the source/dependent, `to` is the target/dependency.
|
|
294
|
-
So `from: A, to: B, type: "depends-on"` means "A depends on B." For `flow` edges, the arrow
|
|
295
|
-
points from `from` to `to`, indicating sequence or data flow direction.
|
|
296
|
-
|
|
297
|
-
**Style conventions:** Use `solid` for active/satisfied relationships, `dashed` for blocked or
|
|
298
|
-
pending dependencies, and `dotted` for weak/optional relationships. Use `animated: true` to
|
|
299
|
-
draw visual attention to critical paths.
|
|
300
|
-
|
|
301
|
-
### Layout: spacing and groups
|
|
302
|
-
|
|
303
|
-
Agents tend to pack boards too tightly. Give nodes room to breathe — readability beats density.
|
|
304
|
-
|
|
305
|
-
- **Default spacing:** leave a clear gap between neighbors — roughly half a node's width
|
|
306
|
-
horizontally and half its height vertically. A 280×180 node reads well with ~360px between
|
|
307
|
-
column origins and ~260px between row origins.
|
|
308
|
-
- **When nodes are connected by edges, space them further apart** so the edge line, its arrowhead,
|
|
309
|
-
and its label are clearly visible in the gap between nodes. Crowded nodes hide the flow — this is
|
|
310
|
-
the most common cause of an unreadable board.
|
|
311
|
-
- **Inside a group, keep the same breathing room** (groups no longer auto-pack children, so the
|
|
312
|
-
spacing you set is what the human sees). Edges between grouped children especially need the gap.
|
|
313
|
-
- **Size a group frame larger than its children's bounding box** so the group header — including the
|
|
314
|
-
node-count badge — stays visible and isn't hidden under the top-left child. For an explicit
|
|
315
|
-
(manual) group frame, add margin on every side (≈56px) plus room at the top for the header rather
|
|
316
|
-
than hugging the children. Auto-fit groups (created without an explicit width/height) already
|
|
317
|
-
reserve this margin.
|
|
318
|
-
|
|
319
|
-
### Colors (Semantic)
|
|
320
|
-
|
|
321
|
-
Use color consistently to convey meaning:
|
|
322
|
-
- **Green** (`#22c55e`) — success, done, healthy
|
|
323
|
-
- **Yellow** (`#eab308`) — in progress, warning, attention needed
|
|
324
|
-
- **Red** (`#ef4444`) — error, blocked, failing
|
|
325
|
-
- **Blue** (`#3b82f6`) — informational, neutral highlight
|
|
326
|
-
- **Gray** (`#6b7280`) — queued, pending, inactive, not yet started
|
|
327
|
-
- **Purple** (`#a855f7`) — special, notable, review needed
|
|
328
|
-
|
|
329
|
-
## MCP Tools Reference
|
|
330
|
-
|
|
331
|
-
PMX Canvas exposes **~21 composable tools**: 12 action-discriminated **composites** (the
|
|
332
|
-
recommended surface) plus a set of first-class standalones. The composites fold the older
|
|
333
|
-
single-purpose tools behind an `action` (and, for `canvas_ax_gate`, a `kind`) discriminator —
|
|
334
|
-
**field names are unchanged**; only the tool name + the `action`/`kind` selector differ.
|
|
335
|
-
|
|
336
|
-
> **Legacy single-purpose tools are Deprecated.** The old names (`canvas_add_node`,
|
|
337
|
-
> `canvas_update_node`, `canvas_request_approval`, `canvas_add_work_item`, …) still work but are
|
|
338
|
-
> marked `Deprecated:` and are **removed in v0.3**. Prefer the composites. The authoritative
|
|
339
|
-
> legacy→composite mapping table lives in [`docs/mcp.md`](../../docs/mcp.md) — this skill does not
|
|
340
|
-
> re-enumerate the deprecated names.
|
|
341
|
-
|
|
342
|
-
### The 12 composites
|
|
343
|
-
|
|
344
|
-
| Composite | `action` values | What it does |
|
|
345
|
-
|-----------|-----------------|--------------|
|
|
346
|
-
| `canvas_node` | `add` · `get` · `update` · `remove` | Create / read / mutate / delete a node |
|
|
347
|
-
| `canvas_render` | `describe-schema` · `validate` · `add-json-render` · `stream-json-render` · `add-graph` | Schema introspection, dry-run validation, and native json-render / graph node creation |
|
|
348
|
-
| `canvas_edge` | `add` · `remove` | Connect / disconnect nodes |
|
|
349
|
-
| `canvas_group` | `create` · `add` · `ungroup` | Manage spatial group containers |
|
|
350
|
-
| `canvas_history` | `undo` · `redo` | Time travel through the mutation ring buffer |
|
|
351
|
-
| `canvas_view` | `arrange` · `focus` · `fit` · `clear` | Auto-arrange, pan-to-node, fit viewport, clear the board |
|
|
352
|
-
| `canvas_query` | `search` · `layout` | Find nodes by keyword, or read full canvas state |
|
|
353
|
-
| `canvas_ax_state` | `get` · `set-focus` · `set-policy` · `report-capability` | Read AX state; set AX focus; patch tool/prompt policy; report host capability |
|
|
354
|
-
| `canvas_ax_work` | `add` · `update` · `annotate` | Canvas-bound work items + review annotations |
|
|
355
|
-
| `canvas_ax_gate` | `request` · `resolve` · `await` × `kind` `approval` \| `elicitation` \| `mode` | The human-decision gate machine (request → await → resolve) |
|
|
356
|
-
| `canvas_ax_timeline` | `read` · `record-event` · `add-evidence` · `send-steering` | The bounded AX diagnostics timeline |
|
|
357
|
-
| `canvas_ax_delivery` | `claim` · `mark` | Adapterless steering delivery (claim → act → mark) |
|
|
358
|
-
|
|
359
|
-
Call shape examples: `canvas_node { action: "add", type, title }`,
|
|
360
|
-
`canvas_view { action: "focus", id }`, `canvas_group { action: "create", childIds }`,
|
|
361
|
-
`canvas_render { action: "add-graph", graphType, data }`,
|
|
362
|
-
`canvas_query { action: "search", query }`,
|
|
363
|
-
`canvas_ax_work { action: "update", id, status }`.
|
|
364
|
-
|
|
365
|
-
`canvas_ax_gate` takes **two** discriminators, `{ kind, action }` — e.g.
|
|
366
|
-
`{ kind: "approval", action: "request", title }`,
|
|
367
|
-
`{ kind: "elicitation", action: "resolve", id, response }`,
|
|
368
|
-
`{ kind: "mode", action: "await", id, timeoutMs }`. (The approval machine-readable action
|
|
369
|
-
identifier is passed as `approvalAction`, since `action` is the lifecycle discriminator.)
|
|
370
|
-
|
|
371
|
-
### Standalones (first-class — not deprecated)
|
|
372
|
-
|
|
373
|
-
These stay separate by design (trust-boundary, firehose, execution-intent, or not-yet-consolidated
|
|
374
|
-
surfaces): `canvas_batch`, `canvas_pin_nodes`, `canvas_screenshot`, `canvas_build_web_artifact`,
|
|
375
|
-
`canvas_open_mcp_app`, `canvas_add_diagram`, `canvas_add_html_node`, `canvas_add_html_primitive`,
|
|
376
|
-
`canvas_refresh_webpage_node`, `canvas_remove_annotation`, `canvas_ax_interaction`,
|
|
377
|
-
`canvas_ingest_activity`, `canvas_invoke_command`, the WebView tools
|
|
378
|
-
(`canvas_webview_start` / `canvas_webview_status` / `canvas_webview_stop`, `canvas_resize`,
|
|
379
|
-
`canvas_evaluate`), and the snapshot tools (`canvas_snapshot`, `canvas_list_snapshots`,
|
|
380
|
-
`canvas_restore`, `canvas_delete_snapshot`, `canvas_gc_snapshots`, `canvas_diff` — a
|
|
381
|
-
`canvas_snapshot` composite is deferred to v0.3).
|
|
382
|
-
|
|
383
|
-
### Node Operations
|
|
384
|
-
|
|
385
|
-
MCP node-type routing — which tool creates which node category:
|
|
386
|
-
|
|
387
|
-
| Node category | MCP creation call |
|
|
388
|
-
|---------------|-------------------|
|
|
389
|
-
| Basic nodes (`markdown`, `status`, `context`, `ledger`, `trace`, `file`, `image`, `webpage`) | `canvas_node { action: "add" }` |
|
|
390
|
-
| `json-render` | `canvas_render { action: "add-json-render" }` |
|
|
391
|
-
| `graph` | `canvas_render { action: "add-graph" }` |
|
|
392
|
-
| `html-primitive` | `canvas_add_html_primitive` (standalone) |
|
|
393
|
-
| `html` | `canvas_add_html_node` (standalone) |
|
|
394
|
-
| `web-artifact` | `canvas_build_web_artifact` (standalone) |
|
|
395
|
-
| `external-app` / tool-backed `mcp-app` | `canvas_open_mcp_app` (standalone) |
|
|
396
|
-
| `group` | `canvas_group { action: "create" }` |
|
|
397
|
-
|
|
398
|
-
If a node type is rejected by `canvas_node { action: "add" }`, call
|
|
399
|
-
`canvas_render { action: "describe-schema" }` and read `mcp.nodeTypeRouting`; do not keep
|
|
400
|
-
retrying the generic add.
|
|
401
|
-
|
|
402
|
-
**`canvas_node { action: "add", … }`** — Add a node to the canvas
|
|
403
|
-
- `type` (required): basic node type only; structured/app/group nodes use the routing table above
|
|
404
|
-
- `title`: short, scannable title
|
|
405
|
-
- `content`: markdown text for most types. For `file`, pass the **file path** (e.g. `"src/auth/login.ts"`) —
|
|
406
|
-
the server auto-loads + watches it. For `image`, pass a file path, URL, or data URI.
|
|
407
|
-
- `path`: compatibility alias for image paths only; prefer `content` for new image calls
|
|
408
|
-
- `x`, `y`: position (prefer omitting for auto-layout); `width`, `height`: dimensions (sensible defaults); `color`: semantic color; `metadata`: arbitrary JSON
|
|
409
|
-
- Returns: `{ id: "<node-id>" }` — capture this ID for edges and groups
|
|
410
|
-
|
|
411
|
-
**`canvas_node { action: "update", id, … }`** — Update an existing node in place (preferred over
|
|
412
|
-
delete+recreate; preserves edges, pins, position)
|
|
413
|
-
- `id` (required), plus any of: `title`, `content`, `x`, `y`, `width`, `height`, `collapsed`, `arrangeLocked`, `data`
|
|
414
|
-
- For `json-render`, pass `spec` to update the rendered spec in place
|
|
415
|
-
- For `graph`, pass graph fields (`graphType`, `data`, `xKey`, `yKey`, `color`, `chartHeight`) to rebuild the chart; `height`/`nodeHeight` set frame geometry, `chartHeight` the chart content
|
|
416
|
-
|
|
417
|
-
**`canvas_node { action: "remove", id }`** — Remove a node and all its connected edges. Clean up nodes that are no longer relevant.
|
|
418
|
-
|
|
419
|
-
**`canvas_node { action: "get", id }`** — Get a single node's full data by `id`.
|
|
420
|
-
|
|
421
|
-
**`canvas_remove_annotation`** (standalone) — Remove a human-drawn annotation by `id`. Use when
|
|
422
|
-
context gives you the annotation ID; use WebView first if you need to identify a mark by shape or location.
|
|
423
|
-
|
|
424
|
-
**`canvas_refresh_webpage_node`** (standalone) — Re-fetch the URL stored on a `webpage` node
|
|
425
|
-
- `id` (required): webpage node to refresh
|
|
426
|
-
- Optional `url`: replace the stored URL before refreshing (use when the human moved the page)
|
|
427
|
-
- Returns the refreshed node with updated `pageTitle` and cached extracted text
|
|
428
|
-
- Use this when a saved canvas is reopened and the agent needs fresh page content without
|
|
429
|
-
losing the node's identity, position, or pins. Example flow:
|
|
430
|
-
|
|
431
|
-
```typescript
|
|
432
|
-
// Add the page once
|
|
433
|
-
canvas_node({ action: 'add', type: 'webpage', url: 'https://example.com/docs' })
|
|
434
|
-
// → returns { id: 'node-abc' }
|
|
435
|
-
|
|
436
|
-
// …later, after the human reopens the canvas…
|
|
437
|
-
canvas_refresh_webpage_node({ id: 'node-abc' })
|
|
438
|
-
// → re-fetches the URL, updates pageTitle + extracted text, keeps the node ID and position
|
|
439
|
-
```
|
|
440
|
-
|
|
441
|
-
**`canvas_render { action: "add-json-render", … }`** — Add a native json-render node
|
|
442
|
-
- Required: `spec`; `title` is optional and inferred from the root element when omitted
|
|
443
|
-
- Prefer a complete json-render object with `root`, `elements`, and optional `state`
|
|
444
|
-
- Legacy bare component specs like `{ type: "Badge", props: {...} }` are accepted and wrapped into a one-element document for compatibility
|
|
445
|
-
- Use this when you want a structured UI panel rendered directly inside PMX Canvas
|
|
446
|
-
- For shadcn `Badge`, prefer `props.text` with variants `default`, `secondary`, `destructive`, or
|
|
447
|
-
`outline`. Legacy `props.label` and status variants (`success`, `info`, `warning`, `error`,
|
|
448
|
-
`danger`) are normalized for saved-spec compatibility.
|
|
449
|
-
|
|
450
|
-
**`canvas_render { action: "stream-json-render", … }`** — Build a json-render node progressively (live)
|
|
451
|
-
- Omit `nodeId` on the first call to create a streaming node (returns its `id`); reuse that `nodeId`
|
|
452
|
-
on later calls to append `patches`; set `done: true` on the final call.
|
|
453
|
-
- `patches` are SpecStream JSON-Patch ops applied server-side (the canvas accumulates the spec), e.g.
|
|
454
|
-
`{ "op": "add", "path": "/elements/card", "value": { … } }`, `{ "op": "replace", "path": "/root", "value": "card" }`.
|
|
455
|
-
- Build incrementally: set `/root`, add container elements, then append child element ids/elements.
|
|
456
|
-
Each call re-renders; partial specs render what they can. Use for dashboards/reports that fill in
|
|
457
|
-
as you generate them rather than appearing all at once.
|
|
458
|
-
|
|
459
|
-
**`canvas_render { action: "add-graph", … }`** — Add a native graph/chart node
|
|
460
|
-
- Required: `graphType`, `data`
|
|
461
|
-
- Supports `line`, `bar`, `pie`, `area`, `scatter`, `radar`, `stacked-bar`, `composed`,
|
|
462
|
-
and the Tufte primitives `sparkline`, `dot-plot`, `bullet`, `slopegraph` (aliases accepted)
|
|
463
|
-
- Use `xKey`/`yKey` for line, bar, area, and scatter graphs
|
|
464
|
-
- Use `zKey` for scatter bubble size
|
|
465
|
-
- Use `nameKey`/`valueKey` for pie graphs
|
|
466
|
-
- Use `axisKey` plus `metrics` for radar graphs
|
|
467
|
-
- Use `series` for stacked-bar graphs
|
|
468
|
-
- Use `barKey`/`lineKey` plus optional `barColor`/`lineColor` for composed graphs
|
|
469
|
-
- Bar charts: `colorBy` (`series` default = one accent + a highlighted bar, `category`, `value`, `none`) and `highlight` (`max`/`min`/index)
|
|
470
|
-
- Use `valueKey` for `sparkline` (plus `fill`/`showEndDot`/`showMinMax`/`showValue`)
|
|
471
|
-
- Use `labelKey`/`valueKey` (plus `sort`) for `dot-plot`
|
|
472
|
-
- Use `labelKey`/`valueKey`/`targetKey`/`rangesKey` for `bullet`
|
|
473
|
-
- Use `labelKey`/`beforeKey`/`afterKey` (plus `beforeLabel`/`afterLabel`/`colorByDirection`) for `slopegraph`
|
|
474
|
-
- Use `nodeHeight` for the canvas frame height and `height` for chart content height
|
|
475
|
-
- Uses the native json-render chart catalog under the hood
|
|
476
|
-
|
|
477
|
-
**Tufte-aware charting** — color must encode data, not decorate. For chart design and critique, use
|
|
478
|
-
the `tufte-viz` skill (`skills/tufte-viz/SKILL.md`). Key rules:
|
|
479
|
-
- Single-series `bar` charts use `colorBy`: default `series` (one accent + one highlighted bar),
|
|
480
|
-
`category` (opt-in palette), `value` (sequential shade by magnitude), or `none` (flat). Do not
|
|
481
|
-
rainbow categorical bars by default.
|
|
482
|
-
- Prefer the Tufte primitives where they fit: `sparkline` (inline trend), `dot-plot` (ranked single
|
|
483
|
-
metric vs. a bar forest), `bullet` (measure vs. target, replaces a gauge), `slopegraph`
|
|
484
|
-
(before/after across many categories).
|
|
485
|
-
- Direct-label data (`showLegend: false`) instead of a legend when one or two series are identifiable.
|
|
486
|
-
- For more than ~4 overlapping series, build small multiples (several small graph nodes on a shared
|
|
487
|
-
scale, arranged in a grid/group) instead of one multi-color chart.
|
|
488
|
-
|
|
489
|
-
**`canvas_build_web_artifact`** (standalone) — Build and optionally open a bundled web artifact
|
|
490
|
-
- Required: `title`, `appTsx` (source string contents, not a file path)
|
|
491
|
-
- CLI `--app-file` reads a file before calling the same build path; MCP callers must pass the source contents
|
|
492
|
-
- Cold builds commonly take 45-60 seconds; use a long client timeout such as 300000 ms or more
|
|
493
|
-
- Returns both `id` and `nodeId` for the created artifact node when `openInCanvas` is true
|
|
494
|
-
|
|
495
|
-
ID extraction for mixed tool responses:
|
|
496
|
-
- Most add-style tools return a flat `id`; web artifacts return `id` plus `nodeId`; snapshots return `id` plus nested `snapshot.id`.
|
|
497
|
-
- Defensive extractor: `const getId = (r) => r.id ?? r.nodeId ?? r.snapshot?.id;`
|
|
498
|
-
|
|
499
|
-
**`canvas_open_mcp_app`** (standalone) — Open a tool-backed external MCP app node
|
|
500
|
-
- Required: `toolName`, `transport`
|
|
501
|
-
- `transport` is either `{ type: "stdio", command, args?, cwd?, env? }` or `{ type: "http", url, headers? }`
|
|
502
|
-
- This is lower-level than `pmx-canvas external-app add --kind excalidraw`; use `canvas_add_diagram` for the built-in Excalidraw preset
|
|
503
|
-
|
|
504
|
-
**`canvas_pin_nodes`** (standalone) — Set, add, or remove pinned context nodes. Use `{ nodeIds: [...] }` — the field is `nodeIds`, not `ids`.
|
|
505
|
-
|
|
506
|
-
**`canvas_diff`** (standalone) — Compare current canvas state with a saved snapshot. Requires `{ snapshot: "<snapshot-id-or-name>" }`; there is no implicit previous-snapshot default.
|
|
507
|
-
|
|
508
|
-
**`canvas_render { action: "describe-schema" }`** — Inspect the running server's create schemas and
|
|
509
|
-
canonical examples. Use before generating structured payloads when you need the authoritative current
|
|
510
|
-
shape; read `mcp.nodeTypeRouting` to choose the right creation call for each node category.
|
|
511
|
-
|
|
512
|
-
**`canvas_render { action: "validate", … }`** — Dry-run a json-render spec or graph payload without
|
|
513
|
-
creating a node. Returns the normalized json-render spec the server would accept.
|
|
514
|
-
|
|
515
|
-
**`canvas_view { action: "fit", … }`** — Fit viewport to all nodes or selected nodes; optional
|
|
516
|
-
`width`, `height`, `padding`, `maxScale`, `nodeIds`. Use before screenshot/whole-board review so the
|
|
517
|
-
server viewport matches the intended camera.
|
|
518
|
-
|
|
519
|
-
**Batch graph creation** — Use the `graph.add` op inside `canvas_batch` / `pmx-canvas batch` for a
|
|
520
|
-
graph node in a larger one-shot build. It takes the same shape as `canvas_render { action: "add-graph" }`.
|
|
521
|
-
In batch/MCP/HTTP payloads, `height` is chart content height and `nodeHeight` is the canvas frame height.
|
|
522
|
-
|
|
523
|
-
### Edge Operations
|
|
524
|
-
|
|
525
|
-
**`canvas_edge { action: "add", … }`** — Connect two nodes
|
|
526
|
-
- `from`, `to` (required): source and target node IDs
|
|
527
|
-
- `fromSearch`, `toSearch`: optional search-based selectors when you do not have IDs. Each search
|
|
528
|
-
query must resolve to exactly one node or the edge creation fails with an ambiguity error.
|
|
529
|
-
- `type`: `flow`, `depends-on`, `relation`, or `references` (default: `relation`)
|
|
530
|
-
- `label`: descriptive relationship label; `style`: `solid`/`dashed`/`dotted`; `animated`: visual emphasis
|
|
531
|
-
- `canvas_edge { action: "remove", id }` removes a connection by edge `id`.
|
|
532
|
-
|
|
533
|
-
### Layout & Navigation
|
|
534
|
-
|
|
535
|
-
- **`canvas_query { action: "layout" }`** — full canvas state (nodes, edges, viewport). Read before mutating.
|
|
536
|
-
- **`canvas_view { action: "arrange", layout }`** — auto-arrange all nodes; `layout` is `grid` (default,
|
|
537
|
-
dashboards/overviews), `column` (vertical lists), or `flow` (horizontal sequences / dependency chains).
|
|
538
|
-
Call once after a batch of adds. For tiered/layered layouts, fine-tune with explicit `x`/`y` via
|
|
539
|
-
`canvas_node { action: "update" }` after arranging.
|
|
540
|
-
- **`canvas_view { action: "focus", id }`** — pan the viewport to a node. Don't focus every node in a
|
|
541
|
-
batch — focus only the final result, or use CLI `focus --no-pan` to select/raise without moving the camera.
|
|
542
|
-
|
|
543
|
-
### Groups
|
|
544
|
-
|
|
545
|
-
- **`canvas_group { action: "create", … }`** — visual container; `title`, `childIds` (node IDs), `color`. Auto-sizes to fit children.
|
|
546
|
-
- **`canvas_group { action: "add", groupId, childIds }`** — add nodes to an existing group.
|
|
547
|
-
- **`canvas_group { action: "ungroup", groupId }`** — release all children from a group.
|
|
548
|
-
|
|
549
|
-
### Group Layout Guidance
|
|
550
|
-
|
|
551
|
-
Use groups as spacious semantic regions, not as tight containers. (Group calls below use
|
|
552
|
-
`canvas_group { action: "create" | "add" | "ungroup" }`.)
|
|
553
|
-
|
|
554
|
-
- Size the child nodes first, especially `graph`, `json-render`, `mcp-app`, image, and webpage
|
|
555
|
-
nodes whose rendered content may need more height than their visible title suggests.
|
|
556
|
-
- Give every group generous interior padding. Reserve extra top padding for the group header, then
|
|
557
|
-
keep children clear of the frame edges so headers, glow, resize handles, and node chrome do not
|
|
558
|
-
visually collide.
|
|
559
|
-
- If creating a group manually, compute its frame from the final child bounds plus padding. If the
|
|
560
|
-
group exists first, expand it before adding large children rather than shrinking children to fit.
|
|
561
|
-
- Use groups to label major regions of a board. Avoid wrapping every small relationship; too many
|
|
562
|
-
tight groups make the canvas harder to read than no groups.
|
|
563
|
-
- Keep edges local to a group where possible. Long cross-board edges can look like they come from
|
|
564
|
-
nowhere; use a nearby bridge/context node or split the relationship into shorter labeled edges.
|
|
565
|
-
- After grouping, verify the result in `canvas_query { action: "layout" }` or the browser: child nodes should be
|
|
566
|
-
fully inside the group with padding, visible nodes should not overlap, and group headers should
|
|
567
|
-
not cover content.
|
|
568
|
-
- If a group makes important content less visible, enlarge the group, split it into clearer
|
|
569
|
-
regions, or remove the group. Visibility is more important than preserving a frame.
|
|
570
|
-
|
|
571
|
-
### Grouped Comparison Boards
|
|
572
|
-
|
|
573
|
-
Use groups as named comparison areas, not just visual boxes.
|
|
574
|
-
|
|
575
|
-
- Create the comparison frame first with `canvas_group { action: "create" }` or
|
|
576
|
-
`pmx-canvas group create`, then add charts, artifacts, and diagrams into that area deliberately.
|
|
577
|
-
- Prefer graph nodes for fast capability demos and side-by-side comparisons. They are lightweight,
|
|
578
|
-
validate quickly, and are easier to regenerate.
|
|
579
|
-
- Prefer web artifacts when the board needs a richer narrative UI, custom interaction, or a more
|
|
580
|
-
polished presentation layer than a graph or json-render node can provide.
|
|
581
|
-
- Use Excalidraw for sketching and flow diagrams, but treat it as less reliable than web-artifact
|
|
582
|
-
app nodes for grouping and rediscovery until `mcp-app` grouping parity is fixed.
|
|
583
|
-
- Native node types are still the most agent-friendly. Graph nodes are the strongest comparison
|
|
584
|
-
primitive today, web artifacts are good but heavier, and Excalidraw / other `mcp-app` nodes are
|
|
585
|
-
useful but still the weakest operationally for create, rediscover, group, and reconnect flows.
|
|
586
|
-
- Leave larger spacing between major regions than you think you need. The spatial analyzer still
|
|
587
|
-
tends to read dense boards as one giant cluster unless groups and gaps are both clear.
|
|
588
|
-
- If you are expanding a board incrementally, verify each add-to-group step instead of assuming
|
|
589
|
-
the node joined the area. Comparison workflows depend on reliable “add this thing to the region
|
|
590
|
-
I’m already building.”
|
|
591
|
-
|
|
592
|
-
Current product caveats for grouped comparison boards:
|
|
593
|
-
- `mcp-app` grouping parity is inconsistent. Web artifacts have grouped cleanly; Excalidraw has
|
|
594
|
-
not always behaved the same way.
|
|
595
|
-
- Search/discoverability for external app nodes can degrade over time in-session, so node IDs are
|
|
596
|
-
safer than title-based rediscovery for follow-up grouping or focus operations.
|
|
597
|
-
- `mcp-app` nodes are less inspectable than native nodes. For graph nodes you can reason from
|
|
598
|
-
structured config, but app nodes often only tell you that an app exists unless you also inspect
|
|
599
|
-
nearby markdown, file, or graph context.
|
|
600
|
-
- Long-running web artifact builds can exceed a short command timeout. When using them in an
|
|
601
|
-
agent workflow, prefer progress-aware handling and avoid assuming a timeout means failure.
|
|
602
|
-
|
|
603
|
-
### Search & Discovery
|
|
604
|
-
|
|
605
|
-
**`canvas_query { action: "search", query }`** — Find nodes by title or content keywords. Returns
|
|
606
|
-
ranked matches with content snippets. Use instead of parsing the full layout to locate specific nodes.
|
|
607
|
-
|
|
608
|
-
### Context Pinning
|
|
609
|
-
|
|
610
|
-
**`canvas_pin_nodes`** (standalone) — Manage pinned context: `nodeIds` (required) plus `mode`
|
|
611
|
-
(`set` replaces all pins, `add`, `remove`).
|
|
612
|
-
- Pinned nodes are the primary human-to-agent communication channel — when a human pins in the
|
|
613
|
-
browser, they're saying "pay attention to these."
|
|
614
|
-
- Best default pin set: one intent-setting markdown node plus 1-3 concrete output nodes.
|
|
615
|
-
- Graph, file, and markdown pins carry richer usable context than `mcp-app` pins. Artifact and
|
|
616
|
-
Excalidraw pins still matter as intent signals, but pair them with a markdown or graph pin so the
|
|
617
|
-
agent understands what is inside the app, not just that it matters.
|
|
618
|
-
|
|
619
|
-
### History & Snapshots
|
|
620
|
-
|
|
621
|
-
- **`canvas_history { action: "undo" }`** / **`canvas_history { action: "redo" }`** — step the mutation ring buffer.
|
|
622
|
-
- **`canvas_snapshot`** (standalone) — save a named snapshot; `name` required. Returns `{ ok, id, snapshot }` (flat `id` aliases `snapshot.id`).
|
|
623
|
-
- **`canvas_restore`** (standalone) — restore from a snapshot `id`.
|
|
624
|
-
- **`canvas_diff`** (standalone) — compare current canvas against a saved snapshot (added/removed/modified nodes & edges).
|
|
625
|
-
|
|
626
|
-
### Canvas Management
|
|
627
|
-
|
|
628
|
-
**`canvas_view { action: "clear" }`** — Remove all nodes and edges. **Always `canvas_snapshot` first** —
|
|
629
|
-
this is irreversible without a prior snapshot.
|
|
630
|
-
|
|
631
|
-
### Browser Automation (WebView)
|
|
632
|
-
|
|
633
|
-
The canvas exposes a headless browser session over MCP for self-inspection and
|
|
634
|
-
automated screenshotting. Use this when you want to (a) verify what the live
|
|
635
|
-
canvas actually looks like after a sequence of mutations, (b) capture an image
|
|
636
|
-
of a freshly-built artifact for the human to review, or (c) drive arbitrary
|
|
637
|
-
JavaScript inside the workbench page.
|
|
638
|
-
|
|
639
|
-
The WebView automation runs on Bun's WebKit-based WebView (macOS) or a headless
|
|
640
|
-
Chromium fallback (Linux). It does **not** open a visible window; it's an
|
|
641
|
-
additional headless renderer attached to the same canvas server, so all five
|
|
642
|
-
tools below operate on the live canvas state.
|
|
643
|
-
|
|
644
|
-
**`canvas_webview_status`** — Inspect the current automation session
|
|
645
|
-
- Returns `{ supported, active, backend, viewportWidth, viewportHeight, url, lastError }`
|
|
646
|
-
- Call before `start` to check whether a session is already alive
|
|
647
|
-
|
|
648
|
-
**`canvas_webview_start`** — Start (or replace) the automation session
|
|
649
|
-
- Optional: `backend` (`webkit` macOS-only, or `chrome`), `width`, `height`
|
|
650
|
-
- The session opens `/workbench` at the canvas URL, waits for the SPA to
|
|
651
|
-
hydrate, and reports back via `canvas_webview_status`
|
|
652
|
-
|
|
653
|
-
**`canvas_webview_stop`** — Tear down the automation session
|
|
654
|
-
|
|
655
|
-
**`canvas_evaluate`** — Run JavaScript inside the workbench page and return the result
|
|
656
|
-
- Required: exactly one of `expression` (single JS expression) or `script` (multi-statement body)
|
|
657
|
-
- `script` is wrapped in an async IIFE, so top-level `await` works inside script bodies
|
|
658
|
-
- Useful for asserting DOM state after a sequence of canvas mutations
|
|
659
|
-
- Do not use `fetch()` inside `canvas_evaluate` to call PMX HTTP APIs; WebView security/CORS
|
|
660
|
-
restrictions can block those requests. Use the matching MCP tools instead.
|
|
661
|
-
- Example: read the count of rendered `.canvas-node` elements:
|
|
662
|
-
|
|
663
|
-
```typescript
|
|
664
|
-
canvas_evaluate({ expression: 'document.querySelectorAll(".canvas-node").length' })
|
|
665
|
-
```
|
|
666
|
-
|
|
667
|
-
Useful workbench selectors:
|
|
668
|
-
- Nodes: `.canvas-node`, `.canvas-node.active`, `.canvas-node.context-pinned`, `.canvas-node.group-node`
|
|
669
|
-
- Node internals: `.node-title`, `.node-titlebar`, `.node-body`, `.node-type-badge`, `.node-controls`
|
|
670
|
-
- Annotations: `.annotation-layer path` renders human-drawn freehand ink. Use WebView
|
|
671
|
-
to inspect or screenshot annotation shapes; MCP/context resources only expose compact
|
|
672
|
-
annotation target summaries, not the raw visual shape. Humans can remove marks with
|
|
673
|
-
the eraser toolbar button; agents can remove a known annotation ID with
|
|
674
|
-
`canvas_remove_annotation`.
|
|
675
|
-
- Canvas chrome: `.hud-layer`, `.canvas-toolbar`, `.connection-dot`, `.canvas-bootstrap-card`
|
|
676
|
-
- Nodes do not expose stable `data-node-id` attributes. Use `canvas_query` (`layout` / `search`) or MCP resource data for exact node IDs.
|
|
677
|
-
|
|
678
|
-
Async script example:
|
|
679
|
-
|
|
680
|
-
```typescript
|
|
681
|
-
canvas_evaluate({
|
|
682
|
-
script: 'const title = await Promise.resolve(document.title); return title;',
|
|
683
|
-
})
|
|
684
|
-
```
|
|
685
|
-
|
|
686
|
-
**`canvas_resize`** — Change the WebView viewport
|
|
687
|
-
- Required: `width`, `height`
|
|
688
|
-
- Use before `canvas_screenshot` when the human needs a specific aspect ratio
|
|
689
|
-
|
|
690
|
-
**`canvas_screenshot`** — Capture a PNG of the current workbench
|
|
691
|
-
- Optional: `format` (`png` default), `fullPage` (boolean)
|
|
692
|
-
- Returns both an MCP image payload (renderable inline by capable agents) and
|
|
693
|
-
a path under `.pmx-canvas/screenshots/` so the human can view the file
|
|
694
|
-
- Pair with `canvas_resize` to control the framing
|
|
695
|
-
|
|
696
|
-
Typical flow when you want to show a result:
|
|
697
|
-
|
|
698
|
-
```typescript
|
|
699
|
-
canvas_webview_start({ width: 1440, height: 900 });
|
|
700
|
-
// …mutations…
|
|
701
|
-
canvas_screenshot({ fullPage: true });
|
|
702
|
-
canvas_webview_stop();
|
|
703
|
-
```
|
|
704
|
-
|
|
705
|
-
### Diagrams (Excalidraw MCP app preset)
|
|
706
|
-
|
|
707
|
-
**`canvas_add_diagram`** — Draw a hand-drawn diagram on the canvas via the hosted
|
|
708
|
-
[Excalidraw MCP app](https://github.com/excalidraw/excalidraw-mcp)
|
|
709
|
-
- Required: `elements` — an array of Excalidraw elements (rectangles, ellipses, diamonds, arrows,
|
|
710
|
-
text). Can also be a JSON-array string.
|
|
711
|
-
- `elements` must be Excalidraw element objects, not Mermaid/DOT/source-text diagrams. Convert source diagrams to Excalidraw elements first or use a markdown/web-artifact node.
|
|
712
|
-
- Optional: `title`, `x`, `y`, `width`, `height`
|
|
713
|
-
- The diagram opens inside an `mcp-app` node with fullscreen editing and draw-on animations
|
|
714
|
-
- CLI equivalent: `pmx-canvas external-app add --kind excalidraw --title "Diagram"`
|
|
715
|
-
- Edits made in expanded/fullscreen mode are persisted back into the node model context and replayed
|
|
716
|
-
when the app iframe remounts.
|
|
717
|
-
- Use this when the human needs a quick sketch, architecture diagram, or flowchart and a
|
|
718
|
-
geometric `graph` node would feel too rigid
|
|
719
|
-
- Prefer labeled shapes (`"label": { "text": "..." }` on rectangle/ellipse/diamond) over
|
|
720
|
-
separate text elements — fewer tokens and auto-centered
|
|
721
|
-
- Do not use separate `text` elements with `containerId`/`boundElements` to place centered text
|
|
722
|
-
inside shapes. The hosted SVG preview does not auto-position those; PMX normalizes imported
|
|
723
|
-
canonical bound text back into shape labels for hosted app calls.
|
|
724
|
-
- For detailed sizing, camera, and label-fit rules, read `references/excalidraw-diagram-authoring.md`
|
|
725
|
-
before creating dense diagrams.
|
|
726
|
-
- Prefer the pastel fill palette in the Excalidraw `read_me` (light blue/green/orange/...) for
|
|
727
|
-
a consistent look across diagrams
|
|
728
|
-
|
|
729
|
-
### External MCP apps (bring your own)
|
|
730
|
-
|
|
731
|
-
**`canvas_open_mcp_app`** — Open any external [MCP Apps](https://modelcontextprotocol.io/docs/extensions/apps)
|
|
732
|
-
server's `ui://` resource as an iframe node on the canvas
|
|
733
|
-
- Required: `toolName`, `transport` (`http` URL or `stdio` command)
|
|
734
|
-
- Optional: `serverName`, `toolArguments`, `title`, `x`, `y`, `width`, `height`
|
|
735
|
-
- Use when no dedicated preset exists yet. The Excalidraw preset (`canvas_add_diagram`) is the
|
|
736
|
-
only one today
|
|
737
|
-
|
|
738
|
-
### Web Artifacts
|
|
739
|
-
|
|
740
|
-
**`canvas_build_web_artifact`** — Build a single-file HTML artifact from React/Tailwind source
|
|
741
|
-
- Required: `title`, `appTsx`
|
|
742
|
-
- Optional: `indexCss`, `mainTsx`, `indexHtml`, extra `files`, `projectPath`, `outputPath`, `deps`, `includeLogs`
|
|
743
|
-
- By default it opens the result on the canvas as an embedded app node
|
|
744
|
-
- By default it returns compact log summaries; set `includeLogs: true` when you need raw stdout/stderr
|
|
745
|
-
- `recharts` is available in the scaffold. For additional libraries, pass CLI `--deps name,name2`
|
|
746
|
-
or MCP/API `deps: ["name"]` before bundling.
|
|
747
|
-
- Failed or empty CLI bundles print `ok: false`, exit non-zero, and do not create a canvas node.
|
|
748
|
-
- Use this when the output should be a richer interactive UI than a simple markdown/file/image node
|
|
749
|
-
- Prefer the dedicated `web-artifacts-builder` skill when you need the full React + shadcn workflow
|
|
750
|
-
- Use the `playwright-cli` skill when you need to validate the built artifact in a live browser
|
|
751
|
-
|
|
752
|
-
### HTML Nodes (Sandboxed iframe)
|
|
753
|
-
|
|
754
|
-
**`canvas_add_html_node`** (standalone) — Add a normal self-contained HTML document rendered in a sandboxed iframe
|
|
755
|
-
- Required: `html` (full document or fragment; inline `<script>` and CDN `<script src="...">` are allowed). If `html` is a bare path to an existing local `.html`/`.htm` file, the server reads that file's contents; otherwise it is treated as raw HTML.
|
|
756
|
-
- Optional: `title`, `summary`, `agentSummary`, `presentation`, `slideTitles`, `embeddedNodeIds`, `embeddedUrls`, `x`, `y`, `width` (default 720), `height` (default 640), `strictSize`
|
|
757
|
-
- Iframe sandbox is `allow-scripts` only — no same-origin access, no top-navigation, no forms
|
|
758
|
-
- Canvas theme tokens are auto-injected as CSS custom properties (both `--c-*` and common `--color-*` aliases such as `--color-text-primary`, `--color-bg`, `--color-accent`) and updated live when the canvas theme changes
|
|
759
|
-
- Use for moderate-complexity visualizations and interactive widgets that need real JS but do not warrant a full React build (Chart.js demos, D3 sketches, custom HTML report views)
|
|
760
|
-
- Normal HTML is the default. Only pass `presentation: true` when the user explicitly asks for a deck/fullscreen presentation; otherwise do not mark raw HTML as presentable.
|
|
761
|
-
- Only presentation-marked HTML nodes expose a browser `Present` button. Use it when the HTML is a deck, briefing, or fullscreen review surface; the PMX shell owns the fullscreen overlay and exits via `Esc` or `Exit presentation`.
|
|
762
|
-
- PMX stores a semantic sidecar (`agentSummary`, `contentSummary`, embedded references) so HTML nodes remain understandable in search, pinned context, and spatial context
|
|
763
|
-
|
|
764
|
-
**`canvas_add_html_primitive`** (standalone) — Generate a reusable HTML communication primitive as a sandboxed `html` node
|
|
765
|
-
- Required: `kind`; run `canvas_render { action: "describe-schema" }` and read `htmlPrimitives` for the current catalog
|
|
766
|
-
- Optional: `title`, `data`, `x`, `y`, `width`, `height`, `strictSize`
|
|
767
|
-
- Use when markdown would be too dense and a structured visual artifact is clearer: tradeoff grids, implementation plans, PR reviews, module maps, design sheets, explainers, reports, and lightweight human-editable boards/editors
|
|
768
|
-
- When the human asks for a PowerPoint-like output, pitch deck, briefing, or presentation, use `kind: "presentation"` unless a bespoke raw HTML deck is required. Include `slides` with short titles, one idea per slide, optional `metrics`, `note` fields for speaker notes, and optional `theme: "canvas" | "midnight" | "paper" | "aurora"` or a custom theme object.
|
|
769
|
-
- For payload patterns, export loops, and the primitive catalog, read `references/html-primitives.md` before creating dense or editable artifacts
|
|
770
|
-
|
|
771
|
-
### Open as Site (standalone surfaces)
|
|
772
|
-
|
|
773
|
-
Any renderable surface node can be opened full-page in its own browser tab — the same
|
|
774
|
-
document it shows in the canvas, just without the node chrome. In the workbench, use the
|
|
775
|
-
↗ **Open as site** button (new tab) or the ⤤ **Open in system browser** button in the
|
|
776
|
-
node title bar (or the expanded overlay). "Open in system browser" launches the real OS
|
|
777
|
-
browser via `POST /api/canvas/open-external` `{ nodeId }` (it opens only this server's own
|
|
778
|
-
surface URL; falls back to a normal new tab when the server can't launch) — use it when
|
|
779
|
-
the host's embedded browser (e.g. Codex) opens `_blank` tabs in-place.
|
|
780
|
-
|
|
781
|
-
- Works for `html` / `html-primitive`, bundled `web-artifact`, `json-render` / `graph`,
|
|
782
|
-
`webpage`, and hosted ext-app `mcp-app` nodes.
|
|
783
|
-
- The tab loads the node's stable surface URL, `/api/canvas/surface/<nodeId>`. The
|
|
784
|
-
in-canvas iframe loads the **exact same URL**, so there is one render path and no
|
|
785
|
-
separate "preview" version — what you see in the canvas is what opens. The URL reflects
|
|
786
|
-
current node state and survives a refresh.
|
|
787
|
-
- Agents can read this URL from any node payload (`canvas_node { action: "get" }` /
|
|
788
|
-
`canvas_query { action: "layout" }`) as `surfaceUrl` — a reliable way to tell a human
|
|
789
|
-
"open the artifact" without disturbing the canvas.
|
|
790
|
-
- Served HTML stays sandboxed (opaque origin via a `Content-Security-Policy: sandbox`
|
|
791
|
-
response header), so opening author code top-level cannot reach the canvas origin.
|
|
792
|
-
- ext-app `mcp-app` nodes open their UI, but interactive tool-calls only work inside the
|
|
793
|
-
canvas (the host bridge has no peer in a bare tab). `webpage` and URL-backed `mcp-app`
|
|
794
|
-
nodes redirect to their external site.
|
|
795
|
-
- This is additive — opening a site never evicts or replaces canvas nodes.
|
|
796
|
-
|
|
797
|
-
### Choosing the Right Visual Tier
|
|
798
|
-
|
|
799
|
-
When the output is more than markdown, pick the lightest tier that fits:
|
|
800
|
-
|
|
801
|
-
| Tier | Tool | Build cost | When to pick it |
|
|
802
|
-
|------|------|------------|-----------------|
|
|
803
|
-
| Declarative UI | `canvas_render` (`add-json-render` / `add-graph`) | None | Schema-driven dashboards, forms, charts; agent-friendly to read back via `canvas_node { action: "get" }` |
|
|
804
|
-
| Generated HTML primitive | `canvas_add_html_primitive` | None | Reusable communication artifacts such as choices, plans, reviews, maps, reports, presentations/decks, and lightweight editors |
|
|
805
|
-
| Sandboxed HTML+JS | `canvas_add_html_node` | None | Self-contained HTML with inline JS or CDN scripts; one-off visualizations or report views |
|
|
806
|
-
| Hosted MCP app | `canvas_open_mcp_app` / `canvas_add_diagram` | None | Interactive editors backed by an external MCP server (e.g. Excalidraw) |
|
|
807
|
-
| Bundled React app | `canvas_build_web_artifact` | Heavy (npm install + bundle) | Multi-component UIs needing React state, routing, shadcn/ui, or Tailwind class composition |
|
|
808
|
-
|
|
809
|
-
### Native Structured UI
|
|
810
|
-
|
|
811
|
-
Use native `json-render` and `graph` nodes when the output should stay fully inside PMX Canvas:
|
|
812
|
-
|
|
813
|
-
1. Use `canvas_render { action: "add-json-render" }` for dashboards, forms, summaries, and interactive UI panels
|
|
814
|
-
2. Use `canvas_render { action: "add-graph" }` for charts and trend visualizations
|
|
815
|
-
3. Use the repo-local `json-render-*` skills when you need help authoring or refining the spec itself
|
|
816
|
-
4. Use `canvas_build_web_artifact` instead when the result needs a full custom React app rather than a schema-driven UI
|
|
817
|
-
|
|
818
|
-
Spec elements support an `on` map (`on.press`, `on.change`, …) binding events to actions (`{ action, params }`) — built-in actions (`setState`, `pushState`, …) or, when named after an AX interaction type, a capability-gated AX emit. e.g. a Button with `on: { press: { action: 'ax.work.create', params: { title: '…' } } }` lets a human turn a panel control into a tracked work item; the viewer forwards it to the canvas, which validates and submits it server-side (clamped to the node's own id). See **Node AX Interactions** above.
|
|
819
|
-
|
|
820
|
-
## MCP Resources
|
|
821
|
-
|
|
822
|
-
These resources give you read access to canvas intelligence. Read them to understand
|
|
823
|
-
what the human has set up and what they're focusing on.
|
|
824
|
-
|
|
825
|
-
| Resource | What it provides |
|
|
826
|
-
|----------|-----------------|
|
|
827
|
-
| `canvas://pinned-context` | Content of pinned nodes + nearby unpinned neighbors |
|
|
828
|
-
| `canvas://schema` | Running-server create schemas and json-render catalog metadata |
|
|
829
|
-
| `canvas://layout` | Full canvas state (viewport, nodes, edges) |
|
|
830
|
-
| `canvas://summary` | Compact overview: node counts by type, pinned titles |
|
|
831
|
-
| `canvas://spatial-context` | Proximity clusters, reading order, pinned neighborhoods |
|
|
832
|
-
| `canvas://history` | Human-readable mutation timeline |
|
|
833
|
-
| `canvas://code-graph` | Auto-detected file import dependencies (JS/TS, Python, Go, Rust) |
|
|
834
|
-
| `canvas://ax` | Host-agnostic AX state: focus, work items, approval gates, review annotations, host capability |
|
|
835
|
-
| `canvas://ax-context` | Agent-ready AX context: pinned context + current focus |
|
|
836
|
-
| `canvas://ax-work` | Canvas-bound AX work: work items, approval gates, review annotations, elicitations, mode requests, and tool/prompt policy |
|
|
837
|
-
| `canvas://ax-timeline` | Bounded AX timeline: recent agent events, evidence, and steering messages |
|
|
838
|
-
| `canvas://ax-pending-steering` | Adapterless delivery: `pending` steering to claim + mark delivered, and `pendingActivity` (open work items / pending approvals / elicitations / mode requests awaiting the agent) |
|
|
839
|
-
| `canvas://ax-delivery` | Steering delivery state (delivered flag) for diagnostics |
|
|
840
|
-
| `canvas://skills` | Index of bundled agent skills shipped with the install. Each skill is also addressable as `canvas://skills/<name>` (e.g. `canvas://skills/web-artifacts-builder`) and returns the full SKILL.md. Read this resource first to discover companion workflows the canvas is built to support. |
|
|
841
|
-
|
|
842
|
-
### Node AX Interactions (capability-gated)
|
|
843
|
-
|
|
844
|
-
Eligible nodes can emit one normalized, validated AX interaction that maps onto an
|
|
845
|
-
AX operation — work item, evidence, approval, review, focus, steering, event,
|
|
846
|
-
elicitation, or mode request. One envelope, many transports:
|
|
847
|
-
|
|
848
|
-
This is the **agent-native nodes** model: existing canvas node types become
|
|
849
|
-
interactive agent controls when their AX capabilities allow it. Do not describe
|
|
850
|
-
this as a separate node type; it is a capability layer on top of markdown,
|
|
851
|
-
status, HTML, json-render, graph, web-artifact, MCP app, and other supported
|
|
852
|
-
nodes.
|
|
853
|
-
|
|
854
|
-
- **Endpoint:** `POST /api/canvas/ax/interaction` with
|
|
855
|
-
`{ type, sourceNodeId, payload }` (MCP: `canvas_ax_interaction`; CLI:
|
|
856
|
-
`pmx-canvas ax interaction`). Returns `{ ok, primitive }` or
|
|
857
|
-
`{ ok: false, code }` if the node type/metadata disallows the type.
|
|
858
|
-
- **Capabilities:** each node type has a default capability set (a ceiling). A
|
|
859
|
-
node may opt in or narrow via `data.axCapabilities` (`{ enabled, allowed }`),
|
|
860
|
-
clamped to the ceiling — a node can never escalate beyond its type's ceiling.
|
|
861
|
-
`html` / `html-primitive`, `mcp-app`, and internal `prompt` / `response` nodes
|
|
862
|
-
are **disabled by default** (opt-in).
|
|
863
|
-
- **Transports:** native node controls call the endpoint directly. Sandboxed
|
|
864
|
-
surfaces emit via a nonce-tagged `postMessage` the parent canvas validates
|
|
865
|
-
before submitting: `html` / `html-primitive` nodes (when opted in) call
|
|
866
|
-
`window.PMX_AX.emit(type, payload)`; **json-render / graph** viewers forward a
|
|
867
|
-
spec action named after an AX type (e.g. `on.press → { action:
|
|
868
|
-
"ax.work.create", params }`, `sourceSurface: 'json-render'`); web-artifact
|
|
869
|
-
**`mcp-app`** nodes use the same parent bridge; external MCP app frames
|
|
870
|
-
(`mode: "ext-app"`) can emit through an injected `window.PMX_AX.emit` with
|
|
871
|
-
Promise acknowledgements, but do not get the read-state bridge. The server
|
|
872
|
-
re-validates capabilities regardless of transport — bridges are convenience,
|
|
873
|
-
not a trust boundary.
|
|
874
|
-
- **Delivery (adapterless):** `canvas://ax-pending-steering` /
|
|
875
|
-
`canvas_ax_delivery { action: "claim" }` return two things, both loop-safe (a consumer never
|
|
876
|
-
receives items it originated):
|
|
877
|
-
- `pending` — undelivered **steering** (directives). Act, then acknowledge with
|
|
878
|
-
`canvas_ax_delivery { action: "mark" }`.
|
|
879
|
-
- `pendingActivity` — open canvas-bound items **awaiting the agent** (open work
|
|
880
|
-
items, pending approval gates / elicitations / mode requests), usually created
|
|
881
|
-
by the human in the browser. These are **state, not steering**: don't
|
|
882
|
-
`canvas_ax_delivery { action: "mark" }` them — resolve each via its gate/work call
|
|
883
|
-
(`canvas_ax_gate { kind: "approval", action: "resolve" }` /
|
|
884
|
-
`canvas_ax_gate { kind: "elicitation", action: "resolve" }` /
|
|
885
|
-
`canvas_ax_gate { kind: "mode", action: "resolve" }` /
|
|
886
|
-
`canvas_ax_work { action: "update" }`).
|
|
887
|
-
- **Contract:** every AX mutation fires `ax-state-changed`, so MCP clients that
|
|
888
|
-
**subscribe** to resources are pushed `canvas://ax-work` / `canvas://ax-context`
|
|
889
|
-
live. Clients that **poll** instead should poll `canvas_ax_delivery { action: "claim" }` —
|
|
890
|
-
`pendingActivity` is how non-steering browser changes reach them. Only steering
|
|
891
|
-
flows through the claim/ack queue.
|
|
892
|
-
- **Steering is gated, not pushed.** A surface button that emits `ax.steer`
|
|
893
|
-
enqueues a steer — it does NOT wake the agent. With a prompt-injecting host
|
|
894
|
-
adapter (e.g. Copilot), it reaches the next turn only when (1) the **pin/focus
|
|
895
|
-
gate is open** (something pinned or focused — so keep a steering board pinned, or
|
|
896
|
-
have its button also emit `ax.focus.set` on itself), (2) a **human message** fires
|
|
897
|
-
the turn, and (3) the agent **acts then acks** (`canvas_ax_delivery { action: "mark" }`),
|
|
898
|
-
or the steer re-injects every gated turn. `GET /api/canvas/ax/context?consumer=<id>` adds
|
|
899
|
-
a compact, loop-safe `delivery: { pendingSteering, pendingActivity }` lead block an
|
|
900
|
-
adapter can inject un-truncated, so steering survives the full-context char clip.
|
|
901
|
-
- **Activity ingestion (bidirectional board):** a host adapter forwards the agent's
|
|
902
|
-
tool/session events with `canvas_ingest_activity` (standalone; HTTP `POST /api/canvas/ax/activity`)
|
|
903
|
-
and the board auto-reacts — `failure`/`error` (or `outcome:"failure"`) → a blocked
|
|
904
|
-
work item + a review finding + `logs` evidence; `tool-result` + `outcome:"success"` →
|
|
905
|
-
`tool-result` evidence; everything else records a timeline event only. Override or
|
|
906
|
-
suppress per call via `reactions` (`{ workItem: false }`, `{ review: { severity } }`, …).
|
|
907
|
-
- **Blocking gates (gates that actually gate):** `canvas_ax_gate` is the request →
|
|
908
|
-
await → resolve machine. After `{ action: "request" }`, call
|
|
909
|
-
`canvas_ax_gate { kind, action: "await", id, timeoutMs }` (HTTP
|
|
910
|
-
`GET /api/canvas/ax/<kind>/<id>?waitMs=`) to BLOCK until the human resolves it in the
|
|
911
|
-
browser or the timeout elapses (`timeoutMs` 0 = immediate read; ≤120000). Use this to
|
|
912
|
-
pause real work on a human decision instead of polling.
|
|
913
|
-
- **Elicitation / mode:** request structured human input
|
|
914
|
-
(`canvas_ax_gate { kind: "elicitation", action: "request" }` →
|
|
915
|
-
`canvas_ax_gate { kind: "elicitation", action: "resolve" }`) or a workflow
|
|
916
|
-
mode transition (`canvas_ax_gate { kind: "mode", action: "request" }` →
|
|
917
|
-
`canvas_ax_gate { kind: "mode", action: "resolve" }`); both are canvas-bound and snapshotted.
|
|
918
|
-
- **Commands:** invoke a registry command — `pmx.plan`, `pmx.execute`,
|
|
919
|
-
`pmx.promote-context`, `pmx.summarize`, `pmx.review` — via
|
|
920
|
-
`canvas_invoke_command` (standalone; HTTP `POST /api/canvas/ax/command`; CLI
|
|
921
|
-
`pmx-canvas ax command invoke`; envelope `ax.command.invoke`). Unknown names
|
|
922
|
-
are rejected; an invocation records an `agent-event` of kind `command`.
|
|
923
|
-
- **Policy:** a canvas-bound, snapshotted tool/prompt policy
|
|
924
|
-
(`tools.allowed|excluded|approvalRequired`, `prompt.systemAppend|mode`) read
|
|
925
|
-
into `canvas://ax-context`. Patch it with `canvas_ax_state { action: "set-policy" }`
|
|
926
|
-
(HTTP `GET|POST /api/canvas/ax/policy`; CLI `pmx-canvas ax policy get|set`); patches
|
|
927
|
-
merge and are normalized server-side.
|
|
928
|
-
|
|
929
|
-
Interactions request PMX-AX primitives only — never arbitrary shell, tool, MCP,
|
|
930
|
-
or host execution.
|
|
931
|
-
|
|
932
|
-
#### Where AX can be used — node capability matrix
|
|
933
|
-
|
|
934
|
-
AX interactions are gated per node type. The lists below are each type's **ceiling**
|
|
935
|
-
— `data.axCapabilities.allowed` can NARROW it, never escalate beyond it.
|
|
936
|
-
|
|
937
|
-
**Enabled by default** (no opt-in needed — an agent/native control can emit straight away):
|
|
938
|
-
|
|
939
|
-
| Node type | Allowed AX interaction types |
|
|
940
|
-
|-----------|------------------------------|
|
|
941
|
-
| `markdown` | `ax.steer`, `ax.work.create`, `ax.evidence.add`, `ax.command.invoke`, `ax.event.record` |
|
|
942
|
-
| `context` | `ax.focus.set`, `ax.steer`, `ax.evidence.add`, `ax.command.invoke`, `ax.event.record` |
|
|
943
|
-
| `status` | `ax.work.create`, `ax.work.update`, `ax.approval.request`, `ax.mode.request`, `ax.event.record` |
|
|
944
|
-
| `file` | `ax.evidence.add`, `ax.review.add`, `ax.focus.set`, `ax.event.record` |
|
|
945
|
-
| `json-render` | `ax.work.create`, `ax.work.update`, `ax.evidence.add`, `ax.elicitation.request`, `ax.event.record` |
|
|
946
|
-
| `graph` | `ax.evidence.add`, `ax.focus.set`, `ax.event.record` |
|
|
947
|
-
| `ledger` | `ax.evidence.add`, `ax.event.record` |
|
|
948
|
-
| `trace` | `ax.evidence.add`, `ax.event.record` |
|
|
949
|
-
| `image` | `ax.evidence.add`, `ax.review.add` |
|
|
950
|
-
| `webpage` | `ax.evidence.add`, `ax.review.add`, `ax.focus.set`, `ax.event.record` |
|
|
951
|
-
| `group` | `ax.focus.set`, `ax.work.create`, `ax.command.invoke`, `ax.event.record` |
|
|
952
|
-
|
|
953
|
-
**Opt-in** — set `axCapabilities.enabled = true` (MCP: pass `axCapabilities` to
|
|
954
|
-
`canvas_add_html_node` / `canvas_node { action: "update" }`. HTTP: `axCapabilities` **and** the
|
|
955
|
-
`html` body are accepted **top-level on both `POST /api/canvas/node` and
|
|
956
|
-
`PATCH /api/canvas/node/<id>`**, or nested under `data` — both work, top-level wins):
|
|
957
|
-
|
|
958
|
-
| Node type | Allowed AX interaction types |
|
|
959
|
-
|-----------|------------------------------|
|
|
960
|
-
| `html` / `html-primitive` | the full set: `ax.work.create`, `ax.work.update`, `ax.steer`, `ax.approval.request`, `ax.review.add`, `ax.evidence.add`, `ax.focus.set`, `ax.elicitation.request`, `ax.mode.request`, `ax.command.invoke`, `ax.event.record` |
|
|
961
|
-
| `mcp-app` (incl. **web-artifact**) | `ax.event.record`, `ax.evidence.add`, `ax.work.create`, `ax.work.update`, `ax.focus.set`, `ax.elicitation.request` |
|
|
962
|
-
|
|
963
|
-
**Never (anchor-only):** internal `prompt` / `response` thread nodes — `ax.event.record`
|
|
964
|
-
only, no human-facing emit.
|
|
965
|
-
|
|
966
|
-
The 11 interaction types and what they create: `ax.work.create` / `ax.work.update`
|
|
967
|
-
(work-queue items; status is exactly one of `todo`, `in-progress`, `blocked`, `done`,
|
|
968
|
-
`cancelled` — **hyphens, not underscores**; `POST`/`PATCH /api/canvas/ax/work` reject an
|
|
969
|
-
unknown token like `in_progress` with `400`), `ax.evidence.add`
|
|
970
|
-
(timeline evidence), `ax.review.add` (review annotation), `ax.focus.set` (agent focus
|
|
971
|
-
pointer), `ax.steer` (a steering message delivered to the agent), `ax.approval.request`
|
|
972
|
-
(approval gate), `ax.elicitation.request` (structured human input), `ax.mode.request`
|
|
973
|
-
(plan/execute/autonomous transition), `ax.command.invoke` (registry command), and
|
|
974
|
-
`ax.event.record` (diagnostic agent-event).
|
|
975
|
-
|
|
976
|
-
#### Building an AX surface in the canvas (emit + reflect)
|
|
977
|
-
|
|
978
|
-
AX surfaces are **composable** — you can build a live work board, review board, or
|
|
979
|
-
inbox as a canvas node that BOTH emits AX interactions AND renders the current AX
|
|
980
|
-
state. The read side mirrors the write side:
|
|
981
|
-
|
|
982
|
-
- **Opt in** (html/mcp-app are off by default): create with
|
|
983
|
-
`canvas_add_html_node({ html, axCapabilities: { enabled: true, allowed: ["ax.work.create","ax.work.update"] } })`,
|
|
984
|
-
or flip an existing node on with
|
|
985
|
-
`canvas_node({ action: "update", id, axCapabilities: { enabled: true, allowed: [...] } })`.
|
|
986
|
-
json-render / graph nodes are enabled by default.
|
|
987
|
-
- **Emit (write):** in `html`, call `window.PMX_AX.emit("ax.work.create", { title })`;
|
|
988
|
-
in `json-render`, bind a control action named after the AX type
|
|
989
|
-
(`on: { press: { action: "ax.work.create", params: { title } } }`).
|
|
990
|
-
- **Confirm (#55):** for `html` / `html-primitive` and PMX_AX-enabled `mcp-app`
|
|
991
|
-
surfaces, `emit` returns a Promise that resolves with the result once the
|
|
992
|
-
canvas acks it, so a button can self-confirm: `const r = await
|
|
993
|
-
window.PMX_AX.emit(...); if (r.ok) showQueued();`. You can also
|
|
994
|
-
`window.PMX_AX.on('ack', cb)` or listen for the `pmx-ax-ack` event. (Falls back
|
|
995
|
-
to an `ax-ack-timeout` result after 10s, so `await` never hangs.)
|
|
996
|
-
- **Reflect (read):** the canvas seeds the surface with a compact AX snapshot at
|
|
997
|
-
load (the same shape as `GET /api/canvas/ax/surface-snapshot`) and live-updates it
|
|
998
|
-
as AX state changes. Works on all three authored surface types:
|
|
999
|
-
- `html` / `html-primitive`: read `window.PMX_AX.state` (`{ focus, workItems,
|
|
1000
|
-
approvalGates, reviewAnnotations, elicitations, modeRequests, policy }`) and
|
|
1001
|
-
subscribe to the `pmx-ax-update` event:
|
|
1002
|
-
`window.addEventListener("pmx-ax-update", e => render(e.detail))`.
|
|
1003
|
-
- `json-render` / `graph`: the snapshot is bound under `/ax`, so a spec reads
|
|
1004
|
-
`{ "$state": "/ax/workItems" }` and it stays live as work items change.
|
|
1005
|
-
- `web-artifact` (mcp-app): the same `window.PMX_AX.state` + `pmx-ax-update` bridge
|
|
1006
|
-
is injected at the `/artifact` route once the node opts in — author the React app
|
|
1007
|
-
against `window.PMX_AX`, not direct `fetch()` (the artifact iframe is sandboxed
|
|
1008
|
-
opaque-origin, so it can't call the API directly).
|
|
1009
|
-
|
|
1010
|
-
Minimal html work board (drop-in via `canvas_add_html_node`, `axCapabilities.enabled: true`):
|
|
1011
|
-
|
|
1012
|
-
```html
|
|
1013
|
-
<button id="add">+ Task</button> <span id="ok"></span>
|
|
1014
|
-
<ul id="q"></ul>
|
|
1015
|
-
<script>
|
|
1016
|
-
function render(s){ document.getElementById('q').innerHTML =
|
|
1017
|
-
((s&&s.workItems)||[]).map(w => '<li>['+w.status+'] '+w.title+'</li>').join(''); }
|
|
1018
|
-
document.getElementById('add').onclick = async () => {
|
|
1019
|
-
const r = await window.PMX_AX.emit('ax.work.create',{title:'New task'});
|
|
1020
|
-
document.getElementById('ok').textContent = r && r.ok ? 'queued ✓' : 'failed'; // #55 self-confirm
|
|
1021
|
-
};
|
|
1022
|
-
render(window.PMX_AX && window.PMX_AX.state);
|
|
1023
|
-
window.addEventListener('pmx-ax-update', e => render(e.detail));
|
|
1024
|
-
</script>
|
|
1025
|
-
```
|
|
1026
|
-
|
|
1027
|
-
This is the right home for a deliberate, interactive AX experience — not the
|
|
1028
|
-
native node buttons. Any agent (via MCP/SDK) can also create/update the same work
|
|
1029
|
-
items, and the board reflects them live.
|
|
1030
|
-
|
|
1031
|
-
> Security note: an AX-enabled surface can READ the whole canvas AX board (all
|
|
1032
|
-
> work items, focus, approval gates, etc. — human review comment text is redacted),
|
|
1033
|
-
> while its EMITS are clamped to its own node. Under the single-workspace
|
|
1034
|
-
> local-trust model this is fine, but don't embed untrusted third-party scripts in
|
|
1035
|
-
> an AX-enabled surface.
|
|
1036
|
-
|
|
1037
|
-
### Reading Spatial Intent
|
|
1038
|
-
|
|
1039
|
-
The `canvas://spatial-context` resource reveals how the human has organized information:
|
|
1040
|
-
|
|
1041
|
-
- **Proximity clusters** — Nodes placed near each other form implicit groups. If the human
|
|
1042
|
-
placed three files next to each other, those files are related in their mental model.
|
|
1043
|
-
- **Reading order** — Nodes sorted top-left to bottom-right, following natural reading flow.
|
|
1044
|
-
This implies sequence or priority.
|
|
1045
|
-
- **Pinned neighborhoods** — For each pinned node, nearby unpinned nodes are listed. These
|
|
1046
|
-
are the human's implicit context — things they consider related to what they pinned.
|
|
1047
|
-
- **Annotations** — Human-drawn markup is summarized by target/bounds only, e.g. an
|
|
1048
|
-
annotation over a node or empty canvas region. Use WebView (`canvas_webview_start` +
|
|
1049
|
-
`canvas_evaluate`/`canvas_screenshot`) when you need to see whether the mark is an
|
|
1050
|
-
arrow, line, circle, or other drawn shape. Remove known annotations with
|
|
1051
|
-
`canvas_remove_annotation`; otherwise use WebView to identify the mark first.
|
|
1052
|
-
- **Board density matters** — On a dense board, spatial context can still read like one large
|
|
1053
|
-
gallery unless groups and spacing separate the major regions clearly.
|
|
1054
|
-
|
|
1055
|
-
Use this spatial intelligence to understand what the human is thinking without them having to
|
|
1056
|
-
explain it explicitly.
|
|
1057
|
-
|
|
1058
|
-
## HTTP API Reference
|
|
1059
|
-
|
|
1060
|
-
All POST/PATCH endpoints accept `Content-Type: application/json`. Default base URL: `http://localhost:4313`
|
|
1061
|
-
|
|
1062
|
-
| Method | Endpoint | Purpose |
|
|
1063
|
-
|--------|----------|---------|
|
|
1064
|
-
| GET | `/api/canvas/state` | Full canvas state |
|
|
1065
|
-
| POST | `/api/canvas/node` | Add node |
|
|
1066
|
-
| PATCH | `/api/canvas/node/<id>` | Update node |
|
|
1067
|
-
| DELETE | `/api/canvas/node/<id>` | Remove node |
|
|
1068
|
-
| POST | `/api/canvas/edge` | Add edge |
|
|
1069
|
-
| DELETE | `/api/canvas/edge` | Remove edge (`{ "edge_id": "..." }`) |
|
|
1070
|
-
| GET | `/api/canvas/snapshots` | List snapshots |
|
|
1071
|
-
| POST | `/api/canvas/snapshots` | Save snapshot |
|
|
1072
|
-
| POST | `/api/canvas/snapshots/<id>` | Restore snapshot |
|
|
1073
|
-
| DELETE | `/api/canvas/snapshots/<id>` | Delete snapshot |
|
|
1074
|
-
| POST | `/api/canvas/context-pins` | Replace pinned nodes |
|
|
1075
|
-
| GET | `/api/canvas/pinned-context` | Get current pins with neighborhood context |
|
|
1076
|
-
| GET | `/api/canvas/search?q=...` | Search nodes |
|
|
1077
|
-
| POST | `/api/canvas/json-render` | Create a native json-render node |
|
|
1078
|
-
| POST | `/api/canvas/json-render/stream` | Create/append a streaming json-render node (SpecStream patches) |
|
|
1079
|
-
| POST | `/api/canvas/graph` | Create a native graph node |
|
|
1080
|
-
| GET | `/api/canvas/schema` | Get running-server create schemas, examples, and json-render catalog metadata |
|
|
1081
|
-
| POST | `/api/canvas/schema/validate` | Validate a json-render spec or graph payload without creating a node |
|
|
1082
|
-
| GET | `/api/canvas/json-render/view?nodeId=...` | View a native json-render or graph node |
|
|
1083
|
-
| POST | `/api/canvas/diagram` | Create an Excalidraw external app node |
|
|
1084
|
-
| POST | `/api/canvas/mcp-app/open` | Open a tool-backed MCP app node |
|
|
1085
|
-
| POST | `/api/canvas/web-artifact` | Build a bundled web artifact and optionally open it on canvas |
|
|
1086
|
-
| POST | `/api/canvas/group` | Create group |
|
|
1087
|
-
| POST | `/api/canvas/group/add` | Add nodes to group |
|
|
1088
|
-
| POST | `/api/canvas/group/ungroup` | Ungroup |
|
|
1089
|
-
| POST | `/api/canvas/arrange` | Auto-arrange |
|
|
1090
|
-
| POST | `/api/canvas/focus` | Center viewport on node |
|
|
1091
|
-
| POST | `/api/canvas/fit` | Fit viewport to canvas bounds or selected nodes |
|
|
1092
|
-
| POST | `/api/canvas/clear` | Clear canvas |
|
|
1093
|
-
| POST | `/api/canvas/update` | Batch update positions |
|
|
1094
|
-
| GET | `/api/canvas/spatial-context` | Spatial clusters and reading order |
|
|
1095
|
-
| POST | `/api/canvas/undo` | Undo |
|
|
1096
|
-
| POST | `/api/canvas/redo` | Redo |
|
|
1097
|
-
| GET | `/api/canvas/history` | Mutation history |
|
|
1098
|
-
| GET | `/api/canvas/code-graph` | File dependency graph |
|
|
1099
|
-
| GET | `/api/workbench/events` | SSE event stream |
|
|
1100
|
-
|
|
1101
|
-
## Workflow Patterns
|
|
1102
|
-
|
|
1103
|
-
These are **operational recipes** — how to sequence canvas calls for a few
|
|
1104
|
-
recurring shapes of work. They are not the project's use cases (those live
|
|
1105
|
-
in the README and are intentionally non-exhaustive). The patterns here exist
|
|
1106
|
-
to make the agent's tool-call sequencing concrete: which MCP tool fires
|
|
1107
|
-
when, what to pin, when to read `canvas://pinned-context`, when to snapshot.
|
|
1108
|
-
|
|
1109
|
-
### Responding to Pinned Context
|
|
1110
|
-
|
|
1111
|
-
When the human pins nodes, they're telling you what matters. This is the most important
|
|
1112
|
-
collaboration pattern:
|
|
1113
|
-
|
|
1114
|
-
1. Read `canvas://pinned-context` — get the content of pinned nodes and their neighborhoods
|
|
1115
|
-
2. Read `canvas://spatial-context` — understand how the whole canvas is organized
|
|
1116
|
-
3. Optionally read `canvas://summary` — see pinned nodes in the context of the full canvas
|
|
1117
|
-
4. Interpret what you find:
|
|
1118
|
-
- What types are the pinned nodes? (files = code focus, status = progress, markdown = concepts)
|
|
1119
|
-
- Are they clustered together (single focus) or spread across the canvas (multi-topic)?
|
|
1120
|
-
- What unpinned nodes are nearby? These are the human's implicit context
|
|
1121
|
-
- What's the reading order? Top-left to bottom-right suggests sequence or priority
|
|
1122
|
-
- If an `mcp-app` node is pinned, treat it as “important but partially opaque” and use nearby
|
|
1123
|
-
graph/file/markdown nodes to recover the missing semantic detail
|
|
1124
|
-
5. Respond by summarizing what you see, what you think the human is focusing on, and ask
|
|
1125
|
-
if they'd like you to act on it (add related nodes, investigate further, etc.)
|
|
1126
|
-
|
|
1127
|
-
**When to use `pinned-context` vs `spatial-context`:**
|
|
1128
|
-
- `canvas://pinned-context` — "what did the human explicitly pin, and what's near those pins?"
|
|
1129
|
-
- `canvas://spatial-context` — "how is the entire canvas organized spatially?"
|
|
1130
|
-
- Read both when you need the full picture; read just `pinned-context` for quick pin checks.
|
|
1131
|
-
|
|
1132
|
-
### Investigation Board
|
|
1133
|
-
|
|
1134
|
-
When debugging, lay out evidence spatially to see connections:
|
|
1135
|
-
|
|
1136
|
-
1. Create a root node describing the bug/issue
|
|
1137
|
-
2. Add evidence nodes: logs, stack traces, relevant code files (use `file` nodes for source)
|
|
1138
|
-
3. Connect evidence to root with `references` edges
|
|
1139
|
-
4. Add a hypothesis node, connect with `flow` edge
|
|
1140
|
-
5. As you investigate, add findings and update connections
|
|
1141
|
-
6. Use `status` nodes to track what you've checked
|
|
1142
|
-
7. Group evidence nodes together, and investigation tasks together
|
|
1143
|
-
8. Arrange with `flow` layout, then fine-tune positions if needed
|
|
1144
|
-
|
|
1145
|
-
```
|
|
1146
|
-
Bug Report ──references──> Error Logs
|
|
1147
|
-
│ │
|
|
1148
|
-
│ references
|
|
1149
|
-
│ ▼
|
|
1150
|
-
└──flow──> Hypothesis ──flow──> Fix
|
|
1151
|
-
│
|
|
1152
|
-
flow
|
|
1153
|
-
▼
|
|
1154
|
-
Verification
|
|
1155
|
-
```
|
|
1156
|
-
|
|
1157
|
-
### Architecture Diagram
|
|
1158
|
-
|
|
1159
|
-
Show system components and how they interact:
|
|
1160
|
-
|
|
1161
|
-
1. Create `markdown` nodes for each service/component (include port, tech stack in content)
|
|
1162
|
-
2. Use `flow` edges for data flow, `depends-on` for dependencies — always label edges
|
|
1163
|
-
3. Group related services with `canvas_group { action: "create" }` (e.g., "Application Services", "Data Layer")
|
|
1164
|
-
4. Use colors: green for healthy, yellow for degraded, red for down
|
|
1165
|
-
5. Arrange with `grid` layout initially
|
|
1166
|
-
6. For tiered architectures, fine-tune with explicit `x`/`y` via `canvas_node { action: "update" }` to show
|
|
1167
|
-
layers (e.g., gateway at top, services in middle, data stores at bottom)
|
|
1168
|
-
7. Connect pipeline stages with `flow` edges where applicable
|
|
1169
|
-
|
|
1170
|
-
### Task Plan with Dependencies
|
|
1171
|
-
|
|
1172
|
-
Track work items and their relationships:
|
|
1173
|
-
|
|
1174
|
-
1. Create `status` nodes for each task
|
|
1175
|
-
2. Color-code: green=done, yellow=in-progress, red=blocked, gray=queued, blue=ready/available
|
|
1176
|
-
3. Connect with `depends-on` edges — use `dashed` style for blocked dependencies, `solid` for
|
|
1177
|
-
satisfied ones
|
|
1178
|
-
4. Update status nodes as work progresses using `canvas_node { action: "update" }`
|
|
1179
|
-
5. Arrange with `flow` layout to show the dependency chain left-to-right
|
|
1180
|
-
6. Group related tasks if the plan has distinct phases
|
|
1181
|
-
|
|
1182
|
-
### Code Exploration
|
|
1183
|
-
|
|
1184
|
-
Understand a codebase by visualizing file relationships:
|
|
1185
|
-
|
|
1186
|
-
1. Add `file` nodes for key source files (content auto-loads and live-updates)
|
|
1187
|
-
2. The code graph auto-detects imports and creates `depends-on` edges automatically — you
|
|
1188
|
-
don't need to manually add import-based edges. You can still add manual edges for
|
|
1189
|
-
conceptual relationships beyond imports (e.g., "middleware validates using jwt")
|
|
1190
|
-
3. Read `canvas://code-graph` for dependency analysis: central files, isolated files
|
|
1191
|
-
4. Group related files with `canvas_group { action: "create" }` (e.g., "Auth Module", "API Routes")
|
|
1192
|
-
5. Pin important files so the human sees them highlighted
|
|
1193
|
-
6. Arrange with `grid` layout after adding files
|
|
1194
|
-
|
|
1195
|
-
### Interactive Artifact Builds
|
|
1196
|
-
|
|
1197
|
-
When the user wants a real browser app instead of static notes:
|
|
1198
|
-
|
|
1199
|
-
1. Use the `web-artifacts-builder` skill if the UI needs React state, routing, or shadcn-style components
|
|
1200
|
-
2. Build with `canvas_build_web_artifact`
|
|
1201
|
-
3. Keep `openInCanvas` enabled unless the user explicitly wants only the output file
|
|
1202
|
-
4. Use the returned `projectPath` as the reusable source workspace for iterations
|
|
1203
|
-
5. Use the returned `path` for sharing or for opening the generated artifact outside the canvas
|
|
1204
|
-
6. Use the `playwright-cli` skill if you need to verify the artifact route or embedded app behavior in a browser
|
|
1205
|
-
|
|
1206
|
-
### Status Dashboard
|
|
1207
|
-
|
|
1208
|
-
Monitor ongoing processes:
|
|
1209
|
-
|
|
1210
|
-
1. Create `status` nodes for each metric/process
|
|
1211
|
-
2. Use semantic colors: green=passing, yellow=running, red=failing, gray=queued
|
|
1212
|
-
3. Connect sequential pipeline stages with `flow` edges (label: "then", "triggers")
|
|
1213
|
-
4. Update nodes in-place as state changes using `canvas_node { action: "update" }` — never delete
|
|
1214
|
-
and recreate, as that loses position and edges
|
|
1215
|
-
5. Arrange with `grid` layout
|
|
1216
|
-
6. The human sees real-time updates via SSE
|
|
1217
|
-
|
|
1218
|
-
### Before/After Comparison
|
|
1219
|
-
|
|
1220
|
-
Show two states side by side for the human to compare:
|
|
1221
|
-
|
|
1222
|
-
1. Take a snapshot before changes: `canvas_snapshot` with name "before-X"
|
|
1223
|
-
2. Make changes to the canvas
|
|
1224
|
-
3. Use `canvas_diff` to show what changed
|
|
1225
|
-
4. Or: create two groups ("Before" and "After") with corresponding nodes
|
|
1226
|
-
|
|
1227
|
-
### Save and Start Fresh
|
|
1228
|
-
|
|
1229
|
-
When the human wants to explore a different approach without losing current work:
|
|
1230
|
-
|
|
1231
|
-
1. **First**, save the current state: `canvas_snapshot` with a descriptive name
|
|
1232
|
-
2. **Then** clear: `canvas_view { action: "clear" }` (never clear without snapshotting first)
|
|
1233
|
-
3. Set up the new workspace with initial nodes
|
|
1234
|
-
4. Tell the human the snapshot name and that `canvas_restore` can bring everything back
|
|
1235
|
-
|
|
1236
|
-
## Best Practices
|
|
1237
|
-
|
|
1238
|
-
1. **Start once, reuse always.** Don't restart the canvas for each task. Build on the
|
|
1239
|
-
existing canvas state.
|
|
1240
|
-
|
|
1241
|
-
2. **Titles are scannable.** Keep titles short (3-6 words). Put details in content.
|
|
1242
|
-
|
|
1243
|
-
3. **Label every edge.** Unlabeled edges lose meaning. "depends on", "calls", "blocks"
|
|
1244
|
-
are all more useful than a bare arrow.
|
|
1245
|
-
|
|
1246
|
-
4. **Auto-arrange after batch adds.** When adding multiple nodes, call
|
|
1247
|
-
`canvas_view { action: "arrange" }` once at the end, not after each node.
|
|
1248
|
-
|
|
1249
|
-
5. **Update in place.** Use `canvas_node { action: "update" }` to change status, content, or
|
|
1250
|
-
color. Don't delete and recreate — that loses position and edges.
|
|
1251
|
-
|
|
1252
|
-
6. **Clean up.** Remove nodes that are no longer relevant. A cluttered canvas is worse
|
|
1253
|
-
than no canvas.
|
|
1254
|
-
|
|
1255
|
-
7. **Read before writing.** Check `canvas://layout` or `canvas_query { action: "layout" }` before
|
|
1256
|
-
adding nodes to avoid duplicates and understand the current state.
|
|
1257
|
-
|
|
1258
|
-
8. **Use pinning.** When you want the human to focus on specific nodes, pin them.
|
|
1259
|
-
When the human pins nodes, read `canvas://pinned-context` to see what they care about.
|
|
1260
|
-
Prefer one intent-setting markdown pin plus a small set of concrete output pins over pinning a
|
|
1261
|
-
whole gallery.
|
|
1262
|
-
|
|
1263
|
-
9. **Snapshot before destructive changes.** Before clearing or major reorganization,
|
|
1264
|
-
save a snapshot so you can restore if needed.
|
|
1265
|
-
|
|
1266
|
-
10. **Prefer MCP tools over HTTP.** When running as an MCP server, use the canvas tools
|
|
1267
|
-
directly rather than shelling out to curl. The tools handle all the details.
|
|
1268
|
-
|
|
1269
|
-
11. **Use groups for visual organization.** When 3+ nodes are related, wrap them in a
|
|
1270
|
-
group to make the relationship visible at a glance.
|
|
1271
|
-
|
|
1272
|
-
12. **Use file nodes for source code.** File nodes auto-watch for changes and update
|
|
1273
|
-
live. This is better than pasting code into markdown nodes.
|
|
1274
|
-
|
|
1275
|
-
13. **Comparison boards need structure, not just content.** For galleries and evaluations, use a
|
|
1276
|
-
named group, give the area breathing room, and keep related charts/artifacts inside that
|
|
1277
|
-
region instead of letting them drift into the main cluster.
|
|
1278
|
-
|
|
1279
|
-
14. **Capture external app IDs immediately.** For Excalidraw and other `mcp-app` nodes, store the
|
|
1280
|
-
returned node ID or pin the node right away. Search/title rediscovery is less reliable there
|
|
1281
|
-
than for markdown, graph, or file nodes.
|
|
1282
|
-
|
|
1283
|
-
15. **Pair app nodes with explainers.** If you create or pin a web artifact or Excalidraw node,
|
|
1284
|
-
add a nearby markdown, graph, or file node that explains what the app is for. This makes
|
|
1285
|
-
pinned context far more useful to later agents.
|
|
141
|
+
Use the visible workbench when the human is actively curating layout:
|
|
142
|
+
|
|
143
|
+
- Drag nodes to move them.
|
|
144
|
+
- Shift+drag empty space to lasso-select.
|
|
145
|
+
- Use the selection bar for Pin as context, Group, Connect, and Clear.
|
|
146
|
+
- Right-click a node for context pinning, position locking, focus, collapse, connect, refresh,
|
|
147
|
+
open, close, and type-specific actions.
|
|
148
|
+
- Drop files or URLs to create matching nodes.
|
|
149
|
+
- Double-click markdown to edit inline.
|
|
150
|
+
- Use toolbar snapshots before experiments and restore only after confirmation.
|
|
151
|
+
|
|
152
|
+
After changing files under `src/client/`, rebuild with `bun run build` before manual browser
|
|
153
|
+
verification.
|
|
154
|
+
|
|
155
|
+
## AX Interactions
|
|
156
|
+
|
|
157
|
+
Node interactions request PMX AX primitives; they never execute arbitrary shell, tools, MCP calls,
|
|
158
|
+
or host actions.
|
|
159
|
+
|
|
160
|
+
- `DEFAULT_NODE_AX_CAPABILITIES` is the per-node-type ceiling.
|
|
161
|
+
- `data.axCapabilities` may enable or narrow capabilities but cannot escalate beyond the ceiling.
|
|
162
|
+
- Sandboxed surfaces are scoped to their own source node.
|
|
163
|
+
- HTML nodes must explicitly opt in.
|
|
164
|
+
- Use `window.PMX_AX.emit(type, payload)` and await its result.
|
|
165
|
+
- Listen for `pmx-ax-update` when an HTML control surface reflects live AX state.
|
|
166
|
+
- Steering is queued; claim with `canvas_ax_delivery`, act, then mark delivered.
|
|
167
|
+
|
|
168
|
+
Read [AX HTML control surfaces](references/ax-html-control-surface.md) before building an
|
|
169
|
+
interactive AX-enabled HTML node.
|
|
170
|
+
|
|
171
|
+
## Resources
|
|
172
|
+
|
|
173
|
+
Read the smallest resource that answers the question:
|
|
174
|
+
|
|
175
|
+
- `canvas://pinned-context` — curated context plus neighborhoods
|
|
176
|
+
- `canvas://summary` — compact board overview
|
|
177
|
+
- `canvas://layout` — complete state
|
|
178
|
+
- `canvas://spatial-context` — clusters and reading order
|
|
179
|
+
- `canvas://history` — mutation history
|
|
180
|
+
- `canvas://code-graph` — detected file dependencies
|
|
181
|
+
- `canvas://ax-context` — compact AX context
|
|
182
|
+
- `canvas://ax-work` — work items and gates
|
|
183
|
+
- `canvas://ax-timeline` — events, evidence, steering
|
|
184
|
+
- `canvas://ax-pending-steering` — adapterless delivery queue
|
|
185
|
+
- `canvas://skills` and `canvas://skills/<name>` — bundled skills
|
|
186
|
+
|
|
187
|
+
Prefer `canvas_query { action: "search" }` over parsing the full layout.
|
|
188
|
+
|
|
189
|
+
## Known Limitations
|
|
190
|
+
|
|
191
|
+
- Hosted MCP-app/ext-app nodes such as Excalidraw require the in-canvas host bridge and are not
|
|
192
|
+
standalone **Open as site** targets. URL-backed viewers and bundled web artifacts remain
|
|
193
|
+
openable.
|
|
194
|
+
- Graph and json-render standalone surfaces use `display=site` and fill the browser viewport.
|
|
195
|
+
- Some hosts cannot automate inside sandboxed workbench iframes. Verify those interactions in a
|
|
196
|
+
system browser or through server-side AX state.
|
|
197
|
+
- `pmx-canvas screenshot` requires an active WebView. Start it with
|
|
198
|
+
`canvas_webview { action: "start" }`.
|
|
199
|
+
- The default server port is 4313, but it may fall back or be explicitly changed.
|
|
1286
200
|
|
|
1287
201
|
## Persistence
|
|
1288
202
|
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
persists across sessions.
|
|
1292
|
-
|
|
1293
|
-
Snapshots, context pins, and large node blobs are stored in the same DB. Web artifacts land in
|
|
1294
|
-
`.pmx-canvas/artifacts/`. Legacy JSON state, snapshot, and blob files are auto-imported into
|
|
1295
|
-
SQLite and renamed to `.bak` on first boot.
|
|
203
|
+
State lives under `.pmx-canvas/`, primarily in `canvas.db`. It includes viewport, nodes, edges,
|
|
204
|
+
annotations, pins, snapshots, AX canvas state, and large-node blobs.
|
|
1296
205
|
|
|
1297
|
-
Stop the server or flush
|
|
1298
|
-
|
|
206
|
+
- Stop the server or close/flush the SDK before committing `canvas.db`.
|
|
207
|
+
- History is session-scoped and is not persisted.
|
|
208
|
+
- Timeline AX data persists independently from canvas snapshots.
|
|
209
|
+
- `canvas_clear` clears canvas-bound state but not host/session diagnostics.
|
|
1299
210
|
|
|
1300
|
-
##
|
|
211
|
+
## Detailed References
|
|
1301
212
|
|
|
1302
|
-
|
|
213
|
+
Load only the reference relevant to the task:
|
|
1303
214
|
|
|
1304
|
-
-
|
|
1305
|
-
-
|
|
1306
|
-
-
|
|
1307
|
-
-
|
|
215
|
+
- [Full MCP, HTTP, CLI, layout, and workflow reference](references/full-reference.md)
|
|
216
|
+
- [Installing PMX Canvas](references/installing-pmx-canvas.md)
|
|
217
|
+
- [HTML primitives](references/html-primitives.md)
|
|
218
|
+
- [Excalidraw diagram authoring](references/excalidraw-diagram-authoring.md)
|
|
219
|
+
- [AX HTML control surfaces](references/ax-html-control-surface.md)
|
|
220
|
+
- [GitHub Copilot adapter](references/github-copilot-app-adapter.md)
|
|
221
|
+
- [Codex app adapter](references/codex-app-adapter.md)
|
|
1308
222
|
|
|
1309
|
-
|
|
1310
|
-
|
|
223
|
+
The authoritative current MCP inventory and legacy replacement table is
|
|
224
|
+
[`docs/mcp.md`](../../docs/mcp.md).
|