pmx-canvas 0.1.5 → 0.1.6
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 +73 -0
- package/Readme.md +325 -68
- package/dist/types/server/canvas-schema.d.ts +2 -0
- package/dist/types/server/index.d.ts +1 -0
- package/package.json +1 -1
- package/skills/pmx-canvas/SKILL.md +179 -12
- package/src/cli/agent.ts +35 -2
- package/src/cli/index.ts +3 -1
- package/src/json-render/server.ts +24 -0
- package/src/mcp/server.ts +15 -5
- package/src/server/canvas-schema.ts +53 -1
- package/src/server/server.ts +6 -0
|
@@ -22,6 +22,7 @@ export interface CanvasCreateTypeSchema {
|
|
|
22
22
|
kind: 'node' | 'virtual-node';
|
|
23
23
|
description: string;
|
|
24
24
|
endpoint: string;
|
|
25
|
+
mcpTool?: string;
|
|
25
26
|
fields: CanvasCreateField[];
|
|
26
27
|
example: Record<string, unknown>;
|
|
27
28
|
notes?: string[];
|
|
@@ -63,6 +64,7 @@ const CANVAS_CREATE_TYPES: CanvasCreateTypeSchema[] = [
|
|
|
63
64
|
kind: 'node',
|
|
64
65
|
description: 'Freeform markdown note.',
|
|
65
66
|
endpoint: '/api/canvas/node',
|
|
67
|
+
mcpTool: 'canvas_add_node',
|
|
66
68
|
fields: [
|
|
67
69
|
{ name: 'title', type: 'string', required: false, description: 'Optional node title.' },
|
|
68
70
|
{ name: 'content', type: 'string', required: false, description: 'Markdown body.' },
|
|
@@ -85,6 +87,7 @@ const CANVAS_CREATE_TYPES: CanvasCreateTypeSchema[] = [
|
|
|
85
87
|
kind: 'node',
|
|
86
88
|
description: 'Compact status indicator.',
|
|
87
89
|
endpoint: '/api/canvas/node',
|
|
90
|
+
mcpTool: 'canvas_add_node',
|
|
88
91
|
fields: [
|
|
89
92
|
{ name: 'title', type: 'string', required: false, description: 'Status label.' },
|
|
90
93
|
{ name: 'content', type: 'string', required: false, description: 'Rendered status text.' },
|
|
@@ -100,6 +103,7 @@ const CANVAS_CREATE_TYPES: CanvasCreateTypeSchema[] = [
|
|
|
100
103
|
kind: 'node',
|
|
101
104
|
description: 'Agent context card container.',
|
|
102
105
|
endpoint: '/api/canvas/node',
|
|
106
|
+
mcpTool: 'canvas_add_node',
|
|
103
107
|
fields: [
|
|
104
108
|
{ name: 'title', type: 'string', required: false, description: 'Optional title override.' },
|
|
105
109
|
{ name: 'content', type: 'string', required: false, description: 'Optional context body.' },
|
|
@@ -115,6 +119,7 @@ const CANVAS_CREATE_TYPES: CanvasCreateTypeSchema[] = [
|
|
|
115
119
|
kind: 'node',
|
|
116
120
|
description: 'Structured ledger/log node.',
|
|
117
121
|
endpoint: '/api/canvas/node',
|
|
122
|
+
mcpTool: 'canvas_add_node',
|
|
118
123
|
fields: [
|
|
119
124
|
{ name: 'title', type: 'string', required: false, description: 'Optional title.' },
|
|
120
125
|
{ name: 'content', type: 'string', required: false, description: 'Ledger body text.' },
|
|
@@ -130,6 +135,7 @@ const CANVAS_CREATE_TYPES: CanvasCreateTypeSchema[] = [
|
|
|
130
135
|
kind: 'node',
|
|
131
136
|
description: 'Execution trace viewer.',
|
|
132
137
|
endpoint: '/api/canvas/node',
|
|
138
|
+
mcpTool: 'canvas_add_node',
|
|
133
139
|
fields: [
|
|
134
140
|
{ name: 'title', type: 'string', required: false, description: 'Optional title.' },
|
|
135
141
|
{ name: 'content', type: 'string', required: false, description: 'Trace summary.' },
|
|
@@ -145,6 +151,7 @@ const CANVAS_CREATE_TYPES: CanvasCreateTypeSchema[] = [
|
|
|
145
151
|
kind: 'node',
|
|
146
152
|
description: 'Workspace file viewer.',
|
|
147
153
|
endpoint: '/api/canvas/node',
|
|
154
|
+
mcpTool: 'canvas_add_node',
|
|
148
155
|
fields: [
|
|
149
156
|
{ name: 'content', type: 'string', required: true, description: 'Workspace-relative or absolute file path.' },
|
|
150
157
|
{ name: 'title', type: 'string', required: false, description: 'Optional title override.' },
|
|
@@ -162,6 +169,7 @@ const CANVAS_CREATE_TYPES: CanvasCreateTypeSchema[] = [
|
|
|
162
169
|
kind: 'node',
|
|
163
170
|
description: 'Image node backed by a path, URL, or data URI.',
|
|
164
171
|
endpoint: '/api/canvas/node',
|
|
172
|
+
mcpTool: 'canvas_add_node',
|
|
165
173
|
fields: [
|
|
166
174
|
{ name: 'content', type: 'string', required: true, description: 'Image path, URL, or data URI.' },
|
|
167
175
|
{ name: 'title', type: 'string', required: false, description: 'Optional title override.' },
|
|
@@ -187,6 +195,7 @@ const CANVAS_CREATE_TYPES: CanvasCreateTypeSchema[] = [
|
|
|
187
195
|
kind: 'node',
|
|
188
196
|
description: 'Persisted webpage snapshot with server-side fetch and refresh.',
|
|
189
197
|
endpoint: '/api/canvas/node',
|
|
198
|
+
mcpTool: 'canvas_add_node',
|
|
190
199
|
fields: [
|
|
191
200
|
{ name: 'url', type: 'string', required: true, description: 'HTTP(S) URL to fetch and cache.', aliases: ['content'] },
|
|
192
201
|
{ name: 'title', type: 'string', required: false, description: 'Optional title override.' },
|
|
@@ -210,6 +219,7 @@ const CANVAS_CREATE_TYPES: CanvasCreateTypeSchema[] = [
|
|
|
210
219
|
kind: 'node',
|
|
211
220
|
description: 'Hosted iframe/app node.',
|
|
212
221
|
endpoint: '/api/canvas/node',
|
|
222
|
+
mcpTool: 'canvas_open_mcp_app',
|
|
213
223
|
fields: [
|
|
214
224
|
{ name: 'title', type: 'string', required: false, description: 'App title.' },
|
|
215
225
|
{ name: 'content', type: 'string', required: false, description: 'Optional inline content.' },
|
|
@@ -223,11 +233,35 @@ const CANVAS_CREATE_TYPES: CanvasCreateTypeSchema[] = [
|
|
|
223
233
|
'Tool-backed MCP app nodes and hosted artifact nodes persist `data.provenance` when the server can infer a reopen or rehydrate path.',
|
|
224
234
|
],
|
|
225
235
|
},
|
|
236
|
+
{
|
|
237
|
+
type: 'external-app',
|
|
238
|
+
kind: 'virtual-node',
|
|
239
|
+
description: 'Tool-backed hosted app opened from an external MCP server, such as Excalidraw.',
|
|
240
|
+
endpoint: '/api/canvas/mcp-app/open',
|
|
241
|
+
mcpTool: 'canvas_open_mcp_app',
|
|
242
|
+
fields: [
|
|
243
|
+
{ name: 'toolName', type: 'string', required: true, description: 'Tool name on the external MCP server.' },
|
|
244
|
+
{ name: 'transport', type: '{ type: "stdio", command, args? } | { type: "http", url, headers? }', required: true, description: 'External MCP transport definition.' },
|
|
245
|
+
{ name: 'toolArguments', type: 'record<string, unknown>', required: false, description: 'Arguments passed to the external MCP tool.' },
|
|
246
|
+
{ name: 'title', type: 'string', required: false, description: 'Optional canvas node title override.' },
|
|
247
|
+
],
|
|
248
|
+
example: {
|
|
249
|
+
toolName: 'create_view',
|
|
250
|
+
transport: { type: 'http', url: 'https://mcp.excalidraw.com/mcp' },
|
|
251
|
+
toolArguments: { elements: '[]' },
|
|
252
|
+
title: 'Excalidraw Diagram',
|
|
253
|
+
},
|
|
254
|
+
notes: [
|
|
255
|
+
'For Excalidraw specifically, prefer canvas_add_diagram because it fills in the built-in transport, toolName, and checkpoint wiring.',
|
|
256
|
+
'The CLI convenience command `external-app add --kind excalidraw` maps to this built-in Excalidraw preset; MCP canvas_open_mcp_app is the lower-level transport form.',
|
|
257
|
+
],
|
|
258
|
+
},
|
|
226
259
|
{
|
|
227
260
|
type: 'group',
|
|
228
261
|
kind: 'node',
|
|
229
262
|
description: 'Canvas group frame.',
|
|
230
263
|
endpoint: '/api/canvas/group',
|
|
264
|
+
mcpTool: 'canvas_create_group',
|
|
231
265
|
fields: [
|
|
232
266
|
{ name: 'title', type: 'string', required: false, description: 'Group title.' },
|
|
233
267
|
{ name: 'childIds', type: 'string[]', required: false, description: 'Initial child node IDs.' },
|
|
@@ -244,6 +278,7 @@ const CANVAS_CREATE_TYPES: CanvasCreateTypeSchema[] = [
|
|
|
244
278
|
kind: 'virtual-node',
|
|
245
279
|
description: 'Native structured UI panel rendered from a validated json-render spec.',
|
|
246
280
|
endpoint: '/api/canvas/json-render',
|
|
281
|
+
mcpTool: 'canvas_add_json_render_node',
|
|
247
282
|
fields: [
|
|
248
283
|
{ name: 'title', type: 'string', required: true, description: 'Rendered node title.' },
|
|
249
284
|
{ name: 'spec', type: 'JsonRenderSpec', required: true, description: 'Complete json-render spec.' },
|
|
@@ -276,6 +311,7 @@ const CANVAS_CREATE_TYPES: CanvasCreateTypeSchema[] = [
|
|
|
276
311
|
kind: 'virtual-node',
|
|
277
312
|
description: 'Native chart node backed by the json-render chart catalog.',
|
|
278
313
|
endpoint: '/api/canvas/graph',
|
|
314
|
+
mcpTool: 'canvas_add_graph_node',
|
|
279
315
|
fields: [
|
|
280
316
|
{
|
|
281
317
|
name: 'graphType',
|
|
@@ -325,6 +361,7 @@ const CANVAS_CREATE_TYPES: CanvasCreateTypeSchema[] = [
|
|
|
325
361
|
kind: 'virtual-node',
|
|
326
362
|
description: 'Bundled single-file HTML artifact that can open as an embedded canvas node.',
|
|
327
363
|
endpoint: '/api/canvas/web-artifact',
|
|
364
|
+
mcpTool: 'canvas_build_web_artifact',
|
|
328
365
|
fields: [
|
|
329
366
|
{ name: 'title', type: 'string', required: true, description: 'Artifact title used for default paths.' },
|
|
330
367
|
{ name: 'appTsx', type: 'string', required: true, description: 'Contents for src/App.tsx. The CLI also accepts piped contents via --stdin.', aliases: ['app-file', 'app-tsx'] },
|
|
@@ -349,6 +386,16 @@ function clone<T>(value: T): T {
|
|
|
349
386
|
return JSON.parse(JSON.stringify(value)) as T;
|
|
350
387
|
}
|
|
351
388
|
|
|
389
|
+
function buildMcpNodeTypeRouting(nodeTypes: CanvasCreateTypeSchema[]): Record<string, string> {
|
|
390
|
+
const routing: Record<string, string> = {};
|
|
391
|
+
for (const entry of nodeTypes) {
|
|
392
|
+
if (typeof entry.mcpTool === 'string') {
|
|
393
|
+
routing[entry.type] = entry.mcpTool;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
return routing;
|
|
397
|
+
}
|
|
398
|
+
|
|
352
399
|
export function describeCanvasSchema(): {
|
|
353
400
|
ok: true;
|
|
354
401
|
source: 'running-server';
|
|
@@ -364,13 +411,15 @@ export function describeCanvasSchema(): {
|
|
|
364
411
|
mcp: {
|
|
365
412
|
tools: string[];
|
|
366
413
|
resources: string[];
|
|
414
|
+
nodeTypeRouting: Record<string, string>;
|
|
367
415
|
};
|
|
368
416
|
} {
|
|
417
|
+
const nodeTypes = clone(CANVAS_CREATE_TYPES);
|
|
369
418
|
return {
|
|
370
419
|
ok: true,
|
|
371
420
|
source: 'running-server',
|
|
372
421
|
version: readPackageVersion(),
|
|
373
|
-
nodeTypes
|
|
422
|
+
nodeTypes,
|
|
374
423
|
jsonRender: {
|
|
375
424
|
rootShape: {
|
|
376
425
|
root: 'string',
|
|
@@ -388,10 +437,13 @@ export function describeCanvasSchema(): {
|
|
|
388
437
|
'canvas_add_json_render_node',
|
|
389
438
|
'canvas_add_graph_node',
|
|
390
439
|
'canvas_build_web_artifact',
|
|
440
|
+
'canvas_open_mcp_app',
|
|
441
|
+
'canvas_create_group',
|
|
391
442
|
'canvas_describe_schema',
|
|
392
443
|
'canvas_validate_spec',
|
|
393
444
|
],
|
|
394
445
|
resources: ['canvas://schema'],
|
|
446
|
+
nodeTypeRouting: buildMcpNodeTypeRouting(nodeTypes),
|
|
395
447
|
},
|
|
396
448
|
};
|
|
397
449
|
}
|
package/src/server/server.ts
CHANGED
|
@@ -1501,6 +1501,12 @@ async function handleCanvasBuildWebArtifact(req: Request): Promise<Response> {
|
|
|
1501
1501
|
bytes: result.fileSize,
|
|
1502
1502
|
projectPath: result.projectPath,
|
|
1503
1503
|
openedInCanvas: result.openedInCanvas,
|
|
1504
|
+
// `id` is the canvas node id alias used by every other add-style
|
|
1505
|
+
// response. It is only present when a canvas node was actually
|
|
1506
|
+
// created (i.e. openInCanvas was not explicitly disabled). When
|
|
1507
|
+
// there is no canvas node, the alias is intentionally omitted so
|
|
1508
|
+
// consumers can `'id' in response` to detect the build-only case.
|
|
1509
|
+
...(typeof result.nodeId === 'string' ? { id: result.nodeId } : {}),
|
|
1504
1510
|
nodeId: result.nodeId,
|
|
1505
1511
|
url: result.url,
|
|
1506
1512
|
metadata: result.metadata,
|