pmx-canvas 0.2.7 → 0.3.1
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 +148 -1
- package/Readme.md +13 -11
- package/dist/canvas/index.js +2 -2
- package/dist/json-render/index.css +1 -1
- package/dist/types/cli/daemon.d.ts +74 -0
- package/dist/types/cli/watch.d.ts +2 -2
- package/dist/types/client/canvas/CanvasViewport.d.ts +1 -1
- package/dist/types/client/canvas/CommandPalette.d.ts +1 -1
- package/dist/types/client/canvas/Minimap.d.ts +1 -1
- package/dist/types/client/nodes/FileNode.d.ts +1 -1
- package/dist/types/client/nodes/ImageNode.d.ts +1 -1
- package/dist/types/client/nodes/InlineFormatBar.d.ts +1 -1
- package/dist/types/client/nodes/MarkdownNode.d.ts +1 -1
- package/dist/types/client/nodes/PromptNode.d.ts +1 -1
- package/dist/types/client/nodes/ResponseNode.d.ts +1 -1
- package/dist/types/mcp/canvas-access.d.ts +0 -7
- package/dist/types/server/ax-state-manager.d.ts +3 -2
- package/dist/types/server/bundled-skills.d.ts +3 -3
- package/dist/types/server/canvas-schema.d.ts +1 -1
- package/dist/types/server/canvas-state.d.ts +2 -0
- package/dist/types/server/ext-app-lookup.d.ts +1 -3
- package/dist/types/server/html-primitives.d.ts +1 -1
- package/dist/types/server/index.d.ts +4 -4
- package/dist/types/server/operations/composites.d.ts +29 -23
- package/dist/types/server/operations/http.d.ts +2 -1
- package/dist/types/server/operations/index.d.ts +1 -1
- package/dist/types/server/operations/ops/ax-read.d.ts +2 -0
- package/dist/types/server/operations/ops/canvas-wire.d.ts +2 -0
- package/dist/types/server/operations/ops/ext-app.d.ts +2 -0
- package/dist/types/shared/ax-intent.d.ts +1 -1
- package/docs/RELEASE.md +23 -8
- package/docs/ax-host-adapter-contract.md +7 -7
- package/docs/ax-state-contract.md +7 -6
- package/docs/bun-webview-integration.md +5 -5
- package/docs/cli.md +1 -1
- package/docs/http-api.md +35 -3
- package/docs/mcp.md +121 -100
- package/docs/node-types.md +45 -35
- package/docs/plans/plan-006-mcp-tool-consolidation.md +3 -1
- package/docs/plans/plan-008-registry-finish.md +2 -0
- package/docs/plans/plan-009-tech-debt-backlog.md +51 -0
- package/docs/screenshot.png +0 -0
- package/docs/tech-debt-assessment-2026-06.md +1 -1
- package/docs/tech-debt-assessment-2026-07.md +135 -0
- package/package.json +5 -2
- package/skills/data-analysis/SKILL.md +3 -3
- package/skills/frontend-design/SKILL.md +3 -2
- package/skills/json-render-mcp/SKILL.md +4 -3
- package/skills/playwright-cli/SKILL.md +1 -1
- package/skills/pmx-canvas/SKILL.md +12 -7
- package/skills/pmx-canvas/evals/evals.json +2 -2
- package/skills/pmx-canvas/references/ax-html-control-surface.md +3 -1
- package/skills/pmx-canvas/references/excalidraw-diagram-authoring.md +2 -2
- package/skills/pmx-canvas/references/full-reference.md +58 -56
- package/skills/pmx-canvas/references/html-primitives.md +3 -3
- package/skills/tufte-viz/SKILL.md +5 -4
- package/skills/web-artifacts-builder/SKILL.md +4 -4
- package/src/cli/agent.ts +1861 -1548
- package/src/cli/daemon.ts +460 -0
- package/src/cli/index.ts +63 -326
- package/src/cli/watch.ts +2 -10
- package/src/client/App.tsx +48 -46
- package/src/client/canvas/AttentionHistory.tsx +11 -1
- package/src/client/canvas/CanvasNode.tsx +41 -29
- package/src/client/canvas/CanvasViewport.tsx +101 -66
- package/src/client/canvas/CommandPalette.tsx +61 -27
- package/src/client/canvas/ContextMenu.tsx +13 -20
- package/src/client/canvas/ContextPinBar.tsx +1 -5
- package/src/client/canvas/ContextPinHud.tsx +1 -6
- package/src/client/canvas/DockedNode.tsx +4 -4
- package/src/client/canvas/EdgeLayer.tsx +37 -36
- package/src/client/canvas/ExpandedNodeOverlay.tsx +69 -45
- package/src/client/canvas/FocusFieldLayer.tsx +20 -22
- package/src/client/canvas/IntentLayer.tsx +31 -14
- package/src/client/canvas/Minimap.tsx +11 -16
- package/src/client/canvas/SelectionBar.tsx +4 -11
- package/src/client/canvas/ShortcutOverlay.tsx +3 -1
- package/src/client/canvas/SnapshotPanel.tsx +77 -95
- package/src/client/canvas/auto-fit.ts +15 -14
- package/src/client/canvas/snap-guides.ts +12 -12
- package/src/client/canvas/use-node-resize.ts +1 -5
- package/src/client/canvas/use-pan-zoom.ts +25 -26
- package/src/client/ext-app/bridge.ts +3 -12
- package/src/client/icons.tsx +63 -20
- package/src/client/nodes/ContextNode.tsx +14 -25
- package/src/client/nodes/ExtAppFrame.tsx +60 -39
- package/src/client/nodes/FileNode.tsx +74 -62
- package/src/client/nodes/GroupNode.tsx +4 -6
- package/src/client/nodes/HtmlNode.tsx +76 -46
- package/src/client/nodes/ImageNode.tsx +18 -27
- package/src/client/nodes/InlineFormatBar.tsx +4 -21
- package/src/client/nodes/InlineMarkdownEditor.tsx +0 -1
- package/src/client/nodes/LedgerNode.tsx +10 -4
- package/src/client/nodes/MarkdownNode.tsx +3 -10
- package/src/client/nodes/McpAppNode.tsx +26 -22
- package/src/client/nodes/MdFormatBar.tsx +10 -7
- package/src/client/nodes/PromptNode.tsx +23 -51
- package/src/client/nodes/ResponseNode.tsx +3 -13
- package/src/client/nodes/StatusNode.tsx +5 -9
- package/src/client/nodes/StatusSummary.tsx +2 -8
- package/src/client/nodes/WebpageNode.tsx +20 -14
- package/src/client/nodes/iframe-document-url.ts +25 -16
- package/src/client/nodes/image-warnings.ts +1 -7
- package/src/client/nodes/md-format.ts +20 -5
- package/src/client/state/attention-bridge.ts +4 -9
- package/src/client/state/attention-store.ts +1 -7
- package/src/client/state/canvas-store.ts +52 -36
- package/src/client/state/intent-bridge.ts +176 -112
- package/src/client/state/intent-store.ts +4 -1
- package/src/client/state/sse-bridge.ts +53 -70
- package/src/json-render/catalog.ts +12 -16
- package/src/json-render/charts/components.tsx +16 -20
- package/src/json-render/charts/extra-components.tsx +8 -16
- package/src/json-render/charts/extra-definitions.ts +1 -2
- package/src/json-render/charts/tufte-components.tsx +37 -20
- package/src/json-render/charts/tufte-definitions.ts +8 -2
- package/src/json-render/renderer/index.tsx +42 -22
- package/src/json-render/schema.ts +6 -3
- package/src/json-render/server.ts +33 -39
- package/src/mcp/canvas-access.ts +40 -121
- package/src/mcp/server.ts +206 -271
- package/src/server/agent-context.ts +63 -36
- package/src/server/ax-context.ts +7 -5
- package/src/server/ax-interaction.ts +176 -43
- package/src/server/ax-state-manager.ts +185 -41
- package/src/server/ax-state.ts +142 -47
- package/src/server/bundled-skills.ts +3 -3
- package/src/server/canvas-db.ts +213 -95
- package/src/server/canvas-operations.ts +177 -120
- package/src/server/canvas-provenance.ts +1 -4
- package/src/server/canvas-schema.ts +500 -102
- package/src/server/canvas-serialization.ts +27 -35
- package/src/server/canvas-state.ts +155 -58
- package/src/server/chart-template.ts +4 -5
- package/src/server/code-graph.ts +20 -7
- package/src/server/diagram-presets.ts +28 -29
- package/src/server/ext-app-lookup.ts +4 -20
- package/src/server/html-node-summary.ts +19 -10
- package/src/server/html-primitives.ts +326 -97
- package/src/server/html-surface.ts +6 -9
- package/src/server/image-source.ts +6 -4
- package/src/server/index.ts +321 -218
- package/src/server/intent-registry.ts +3 -6
- package/src/server/mcp-app-candidate.ts +5 -10
- package/src/server/mcp-app-host.ts +14 -38
- package/src/server/mcp-app-runtime.ts +12 -20
- package/src/server/mutation-history.ts +15 -5
- package/src/server/operations/composites.ts +38 -40
- package/src/server/operations/http.ts +5 -5
- package/src/server/operations/index.ts +7 -1
- package/src/server/operations/invoker.ts +4 -3
- package/src/server/operations/mcp.ts +29 -36
- package/src/server/operations/ops/annotation.ts +122 -10
- package/src/server/operations/ops/app.ts +102 -77
- package/src/server/operations/ops/ax-await.ts +17 -10
- package/src/server/operations/ops/ax-read.ts +347 -0
- package/src/server/operations/ops/ax-shared.ts +2 -7
- package/src/server/operations/ops/ax-state.ts +32 -14
- package/src/server/operations/ops/ax-timeline.ts +32 -19
- package/src/server/operations/ops/ax-work.ts +54 -37
- package/src/server/operations/ops/batch.ts +39 -14
- package/src/server/operations/ops/canvas-wire.ts +91 -0
- package/src/server/operations/ops/edges.ts +37 -25
- package/src/server/operations/ops/ext-app.ts +346 -0
- package/src/server/operations/ops/groups.ts +49 -20
- package/src/server/operations/ops/intent.ts +18 -12
- package/src/server/operations/ops/json-render.ts +239 -98
- package/src/server/operations/ops/nodes.ts +300 -111
- package/src/server/operations/ops/query.ts +62 -33
- package/src/server/operations/ops/snapshots.ts +35 -26
- package/src/server/operations/ops/validate.ts +2 -1
- package/src/server/operations/ops/viewport.ts +60 -16
- package/src/server/operations/ops/webview.ts +44 -18
- package/src/server/operations/registry.ts +2 -3
- package/src/server/operations/types.ts +7 -5
- package/src/server/operations/webview-runner.ts +1 -3
- package/src/server/placement.ts +8 -18
- package/src/server/server.ts +121 -1012
- package/src/server/spatial-analysis.ts +39 -25
- package/src/server/trace-manager.ts +3 -8
- package/src/server/web-artifacts.ts +23 -27
- package/src/server/webpage-node.ts +5 -13
- package/src/shared/auto-arrange.ts +12 -5
- package/src/shared/ax-intent.ts +1 -1
- package/src/shared/content-height-reporter.ts +8 -6
- package/src/shared/ext-app-tool-result.ts +2 -6
- package/src/shared/placement.ts +1 -4
- package/src/shared/semantic-attention.ts +39 -37
- package/src/shared/surface.ts +8 -4
package/src/mcp/server.ts
CHANGED
|
@@ -20,14 +20,15 @@
|
|
|
20
20
|
* ```
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
+
import { readFileSync } from 'node:fs';
|
|
24
|
+
import { dirname, join } from 'node:path';
|
|
25
|
+
import { fileURLToPath } from 'node:url';
|
|
23
26
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
24
27
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
25
28
|
import { z } from 'zod';
|
|
26
29
|
import { canvasState, describeCanvasSchema } from '../server/index.js';
|
|
27
30
|
import { AX_INTERACTION_TYPES } from '../server/ax-interaction.js';
|
|
28
31
|
import { buildPendingAxActivity } from '../server/ax-state.js';
|
|
29
|
-
import { isHtmlPrimitiveKind } from '../server/html-primitives.js';
|
|
30
|
-
import type { HtmlPrimitiveKind } from '../server/html-primitives.js';
|
|
31
32
|
import { registerOperationTools, registerCompositeTools } from '../server/operations/index.js';
|
|
32
33
|
import { createCanvasAccess, refreshCanvasAccess, type CanvasAccess } from './canvas-access.js';
|
|
33
34
|
import { serializeNodeForAgentContext } from '../server/agent-context.js';
|
|
@@ -36,7 +37,6 @@ import {
|
|
|
36
37
|
getCanvasNodeTitle,
|
|
37
38
|
serializeCanvasLayoutForAgent,
|
|
38
39
|
serializeCanvasNode,
|
|
39
|
-
serializeCanvasNodeForAgent,
|
|
40
40
|
summarizeCanvasAnnotationForContext,
|
|
41
41
|
} from '../server/canvas-serialization.js';
|
|
42
42
|
import { listBundledSkills, readBundledSkill } from '../server/bundled-skills.js';
|
|
@@ -46,7 +46,18 @@ let resourceNotificationServer: McpServer | null = null;
|
|
|
46
46
|
let localResourceNotificationsStarted = false;
|
|
47
47
|
let remoteResourceNotificationsBaseUrl: string | null = null;
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
// Resolved from the sibling package.json so it stays accurate through bunx,
|
|
50
|
+
// global npm installs, and repo-local runs (no hard-coded string, no build
|
|
51
|
+
// step required) — same mechanism as --version in src/cli/index.ts.
|
|
52
|
+
function readPackageVersion(): string {
|
|
53
|
+
try {
|
|
54
|
+
const pkgPath = join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'package.json');
|
|
55
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as { version?: string };
|
|
56
|
+
return pkg.version ?? 'unknown';
|
|
57
|
+
} catch {
|
|
58
|
+
return 'unknown';
|
|
59
|
+
}
|
|
60
|
+
}
|
|
50
61
|
|
|
51
62
|
function structuredSchemaDescription(): string {
|
|
52
63
|
const routing = describeCanvasSchema().mcp.nodeTypeRouting;
|
|
@@ -55,10 +66,6 @@ function structuredSchemaDescription(): string {
|
|
|
55
66
|
.join(', ');
|
|
56
67
|
}
|
|
57
68
|
|
|
58
|
-
// workspaceRoot / isPathInside / safeWorkspacePath removed with the
|
|
59
|
-
// canvas_build_web_artifact MCP tool (plan-008 Wave 4). The webartifact.build op
|
|
60
|
-
// sandboxes projectPath/outputPath via web-artifacts.ts resolveWorkspacePath.
|
|
61
|
-
|
|
62
69
|
async function ensureCanvas(): Promise<CanvasAccess> {
|
|
63
70
|
if (!canvas) {
|
|
64
71
|
canvas = await createCanvasAccess();
|
|
@@ -190,12 +197,19 @@ function compactNodePayload(node: Awaited<ReturnType<CanvasAccess['getNode']>>):
|
|
|
190
197
|
};
|
|
191
198
|
}
|
|
192
199
|
|
|
193
|
-
function compactLayoutPayload(
|
|
200
|
+
function compactLayoutPayload(
|
|
201
|
+
layout: Awaited<ReturnType<CanvasAccess['getLayout']>>,
|
|
202
|
+
pinnedIds: string[],
|
|
203
|
+
): Record<string, unknown> {
|
|
194
204
|
return {
|
|
195
205
|
summary: buildSummaryFromLayout(layout, pinnedIds),
|
|
196
206
|
viewport: layout.viewport,
|
|
197
|
-
annotations: (layout.annotations ?? []).map((annotation) =>
|
|
198
|
-
|
|
207
|
+
annotations: (layout.annotations ?? []).map((annotation) =>
|
|
208
|
+
summarizeCanvasAnnotationForContext(annotation, layout.nodes),
|
|
209
|
+
),
|
|
210
|
+
nodes: layout.nodes
|
|
211
|
+
.map((node) => compactNodePayload(node))
|
|
212
|
+
.filter((node): node is Record<string, unknown> => node !== null),
|
|
199
213
|
edges: layout.edges.map((edge) => ({
|
|
200
214
|
id: edge.id,
|
|
201
215
|
from: edge.from,
|
|
@@ -211,24 +225,16 @@ function compactLayoutPayload(layout: Awaited<ReturnType<CanvasAccess['getLayout
|
|
|
211
225
|
function agentSafeFullLayoutPayload(layout: Awaited<ReturnType<CanvasAccess['getLayout']>>): Record<string, unknown> {
|
|
212
226
|
return {
|
|
213
227
|
...serializeCanvasLayoutForAgent(layout),
|
|
214
|
-
annotations: (layout.annotations ?? []).map((annotation) =>
|
|
228
|
+
annotations: (layout.annotations ?? []).map((annotation) =>
|
|
229
|
+
summarizeCanvasAnnotationForContext(annotation, layout.nodes),
|
|
230
|
+
),
|
|
215
231
|
};
|
|
216
232
|
}
|
|
217
233
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
const node = await c.getNode(id);
|
|
223
|
-
if (!node) return { ok: true, id, nodeId: id };
|
|
224
|
-
if (!wantsFullPayload(options)) {
|
|
225
|
-
return { ok: true, node: compactNodePayload(node), id, nodeId: id };
|
|
226
|
-
}
|
|
227
|
-
const serialized = serializeCanvasNodeForAgent(node);
|
|
228
|
-
return { ok: true, node: serialized, ...serialized, nodeId: node.id };
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
function buildSummaryFromLayout(layout: Awaited<ReturnType<CanvasAccess['getLayout']>>, pinnedIds: string[]): Record<string, unknown> {
|
|
234
|
+
function buildSummaryFromLayout(
|
|
235
|
+
layout: Awaited<ReturnType<CanvasAccess['getLayout']>>,
|
|
236
|
+
pinnedIds: string[],
|
|
237
|
+
): Record<string, unknown> {
|
|
232
238
|
const pinned = new Set(pinnedIds);
|
|
233
239
|
const nodesByType: Record<string, number> = {};
|
|
234
240
|
const pinnedTitles: string[] = [];
|
|
@@ -241,7 +247,9 @@ function buildSummaryFromLayout(layout: Awaited<ReturnType<CanvasAccess['getLayo
|
|
|
241
247
|
totalNodes: layout.nodes.length,
|
|
242
248
|
totalEdges: layout.edges.length,
|
|
243
249
|
totalAnnotations: (layout.annotations ?? []).length,
|
|
244
|
-
annotations: (layout.annotations ?? []).map((annotation) =>
|
|
250
|
+
annotations: (layout.annotations ?? []).map((annotation) =>
|
|
251
|
+
summarizeCanvasAnnotationForContext(annotation, layout.nodes),
|
|
252
|
+
),
|
|
245
253
|
nodesByType,
|
|
246
254
|
pinnedCount: pinned.size,
|
|
247
255
|
pinnedTitles,
|
|
@@ -252,15 +260,19 @@ function buildSummaryFromLayout(layout: Awaited<ReturnType<CanvasAccess['getLayo
|
|
|
252
260
|
export async function startMcpServer(): Promise<void> {
|
|
253
261
|
const server = new McpServer({
|
|
254
262
|
name: 'pmx-canvas',
|
|
255
|
-
version:
|
|
263
|
+
version: readPackageVersion(),
|
|
256
264
|
});
|
|
257
265
|
resourceNotificationServer = server;
|
|
258
266
|
|
|
259
267
|
// ── Operation-registry tools (plan-005) ────────────────────────
|
|
260
|
-
//
|
|
261
|
-
//
|
|
262
|
-
//
|
|
263
|
-
//
|
|
268
|
+
// Standalone tools are registered from the shared operation registry for
|
|
269
|
+
// every op not folded into a composite (registerOperationTools skips
|
|
270
|
+
// composite-folded ops — see compositeFoldedOpNames in composites.ts).
|
|
271
|
+
// canvas_get_layout / canvas_get_node / canvas_add_node / canvas_update_node /
|
|
272
|
+
// canvas_remove_node were folded into canvas_query / canvas_node and removed
|
|
273
|
+
// as standalone tools in v0.3.0; they remain reachable via their composite
|
|
274
|
+
// and canvas_batch. Remaining standalone tool names and compact/full payload
|
|
275
|
+
// behavior are frozen (tests/unit/mcp-tool-freeze.test.ts, operation-parity.test.ts).
|
|
264
276
|
registerOperationTools(server, ensureCanvas);
|
|
265
277
|
|
|
266
278
|
// ── Composite (action-discriminated) tools (plan-006) ───────────
|
|
@@ -268,177 +280,51 @@ export async function startMcpServer(): Promise<void> {
|
|
|
268
280
|
// (canvas_node, canvas_render, canvas_edge, canvas_group, canvas_history,
|
|
269
281
|
// canvas_view, canvas_query, plus the AX composites). Each action dispatches
|
|
270
282
|
// to the same registered operation as its standalone tool, so behavior is
|
|
271
|
-
// identical.
|
|
272
|
-
// removed
|
|
273
|
-
// deferred to v0.
|
|
283
|
+
// identical. The legacy single-purpose tools these composites replace are
|
|
284
|
+
// removed as of v0.3.0 per docs/api-stability.md. (canvas_snapshot composite
|
|
285
|
+
// is deferred to v0.4 — its name is still held by the legacy save-snapshot
|
|
286
|
+
// tool; the 6 snapshot standalones are deprecated in v0.3.0 and fold into it.)
|
|
274
287
|
registerCompositeTools(server, ensureCanvas);
|
|
275
288
|
|
|
276
|
-
// ── canvas_add_html_node ────────────────────────────────────────
|
|
277
|
-
server.tool(
|
|
278
|
-
'canvas_add_html_node',
|
|
279
|
-
'Deprecated: use canvas_node with action "add" and type:"html". Add a normal html node: a self-contained HTML document (with optional inline <script> and CDN <script src="...">) rendered inside a sandboxed iframe (sandbox="allow-scripts"). This is the default HTML surface for reports, widgets, and bespoke visualizations. Presentation mode is opt-in: only pass presentation:true when the user explicitly asks for a deck/fullscreen presentation, or use canvas_add_html_primitive with kind="presentation". The iframe inherits live canvas theme tokens via injected CSS custom properties (both --c-* and common --color-* aliases) so authored HTML using var(--color-text-secondary), var(--color-bg), etc. renders cohesively. No same-origin access; no top-navigation; no forms. For declarative-only views with zero JS, prefer canvas_add_json_render_node. For React + shadcn + routing or multi-component apps, use canvas_build_web_artifact.',
|
|
280
|
-
{
|
|
281
|
-
html: z.string().describe('HTML document or fragment. Full <html>...</html> documents are passed through with theme styles injected into <head>; bare fragments are wrapped in a minimal document. Inline <script> and remote CDN <script src="..."> are allowed. If this is a bare path to an existing local .html/.htm file, the file contents are read and used as the HTML.'),
|
|
282
|
-
title: z.string().optional().describe('Node title shown in the canvas titlebar.'),
|
|
283
|
-
summary: z.string().optional().describe('Agent-readable semantic summary for this HTML node. If omitted, PMX derives one from visible HTML text.'),
|
|
284
|
-
agentSummary: z.string().optional().describe('Explicit agent-readable summary. Alias for summary with higher priority when both are provided.'),
|
|
285
|
-
description: z.string().optional().describe('Short description included in search and pinned/spatial context.'),
|
|
286
|
-
presentation: z.boolean().optional().describe('Marks this HTML surface as a fullscreen presentation/deck. Omit unless the user explicitly requested presentation mode.'),
|
|
287
|
-
slideTitles: z.array(z.string()).optional().describe('Agent-readable slide titles for presentation HTML.'),
|
|
288
|
-
embeddedNodeIds: z.array(z.string()).optional().describe('Canvas node IDs embedded or represented by this HTML surface.'),
|
|
289
|
-
embeddedUrls: z.array(z.string()).optional().describe('URLs embedded or represented by this HTML surface.'),
|
|
290
|
-
x: z.number().optional().describe('X position (auto-placed if omitted).'),
|
|
291
|
-
y: z.number().optional().describe('Y position (auto-placed if omitted).'),
|
|
292
|
-
width: z.number().optional().describe('Width in pixels (default: 720).'),
|
|
293
|
-
height: z.number().optional().describe('Height in pixels (default: 640).'),
|
|
294
|
-
strictSize: z.boolean().optional().describe('Keep explicit width/height fixed; iframe scrolls overflow internally.'),
|
|
295
|
-
axCapabilities: z.object({
|
|
296
|
-
enabled: z.boolean().optional(),
|
|
297
|
-
allowed: z.array(z.string()).optional().describe('AX interaction types this node may emit (e.g. ax.work.create, ax.work.update, ax.steer, ax.focus.set, ax.evidence.add, ax.event.record). Clamped to the html capability ceiling server-side; cannot escalate.'),
|
|
298
|
-
}).optional().describe('Opt this html node into AX interactions so its sandboxed UI can emit ax.* via window.PMX_AX.emit(type, payload) (and reflect live AX state). html nodes are AX-disabled by default; set { enabled: true, allowed: [...] } to turn the bridge on. Build interactive boards (work queues, review boards, inboxes) this way.'),
|
|
299
|
-
full: z.boolean().optional().describe('Return the full created node payload. Default false returns compact metadata.'),
|
|
300
|
-
verbose: z.boolean().optional().describe('Alias for full:true.'),
|
|
301
|
-
},
|
|
302
|
-
async (input) => {
|
|
303
|
-
const c = await ensureCanvas();
|
|
304
|
-
const id = await c.addHtmlNode({
|
|
305
|
-
html: input.html,
|
|
306
|
-
...(typeof input.title === 'string' ? { title: input.title } : {}),
|
|
307
|
-
...(input.axCapabilities ? { axCapabilities: input.axCapabilities } : {}),
|
|
308
|
-
...(typeof input.summary === 'string' ? { summary: input.summary } : {}),
|
|
309
|
-
...(typeof input.agentSummary === 'string' ? { agentSummary: input.agentSummary } : {}),
|
|
310
|
-
...(typeof input.description === 'string' ? { description: input.description } : {}),
|
|
311
|
-
...(input.presentation === true ? { presentation: true } : {}),
|
|
312
|
-
...(Array.isArray(input.slideTitles) ? { slideTitles: input.slideTitles } : {}),
|
|
313
|
-
...(Array.isArray(input.embeddedNodeIds) ? { embeddedNodeIds: input.embeddedNodeIds } : {}),
|
|
314
|
-
...(Array.isArray(input.embeddedUrls) ? { embeddedUrls: input.embeddedUrls } : {}),
|
|
315
|
-
...(typeof input.x === 'number' ? { x: input.x } : {}),
|
|
316
|
-
...(typeof input.y === 'number' ? { y: input.y } : {}),
|
|
317
|
-
...(typeof input.width === 'number' ? { width: input.width } : {}),
|
|
318
|
-
...(typeof input.height === 'number' ? { height: input.height } : {}),
|
|
319
|
-
...(input.strictSize === true ? { strictSize: true } : {}),
|
|
320
|
-
});
|
|
321
|
-
return {
|
|
322
|
-
content: [{ type: 'text', text: JSON.stringify(await createdNodePayload(c, id, input), null, 2) }],
|
|
323
|
-
};
|
|
324
|
-
},
|
|
325
|
-
);
|
|
326
|
-
|
|
327
|
-
server.tool(
|
|
328
|
-
'canvas_add_html_primitive',
|
|
329
|
-
'Deprecated: use canvas_node with action "add", type:"html", primitive:"<kind>" (and data). Create a reusable HTML communication primitive as a normal sandboxed html node. Use this instead of long markdown for side-by-side choices, implementation plans, PR review sheets, module maps, design sheets, component galleries, flowcharts, explainers, status reports, and throwaway editors with export/copy paths. Use kind="presentation" only when the user explicitly asks for a PowerPoint-like deck, pitch, briefing, workshop walkthrough, or fullscreen story.',
|
|
330
|
-
{
|
|
331
|
-
kind: htmlPrimitiveKindSchema.describe('Primitive kind. Call canvas_describe_schema and read htmlPrimitives for data shapes and examples.'),
|
|
332
|
-
title: z.string().optional().describe('Node title shown in the canvas titlebar.'),
|
|
333
|
-
data: z.record(z.string(), z.unknown()).optional().describe('Primitive-specific data payload. For kind="presentation", data may include theme:"canvas"|"midnight"|"paper"|"aurora" or a custom color object. See canvas_describe_schema.htmlPrimitives for each shape.'),
|
|
334
|
-
x: z.number().optional().describe('X position (auto-placed if omitted).'),
|
|
335
|
-
y: z.number().optional().describe('Y position (auto-placed if omitted).'),
|
|
336
|
-
width: z.number().optional().describe('Width in pixels (defaults per primitive).'),
|
|
337
|
-
height: z.number().optional().describe('Height in pixels (defaults per primitive).'),
|
|
338
|
-
strictSize: z.boolean().optional().describe('Keep explicit width/height fixed; iframe scrolls overflow internally.'),
|
|
339
|
-
full: z.boolean().optional().describe('Return the full created node payload. Default false returns compact metadata.'),
|
|
340
|
-
verbose: z.boolean().optional().describe('Alias for full:true.'),
|
|
341
|
-
},
|
|
342
|
-
async (input) => {
|
|
343
|
-
const c = await ensureCanvas();
|
|
344
|
-
const kind = input.kind as HtmlPrimitiveKind;
|
|
345
|
-
const result = await c.addHtmlPrimitive({
|
|
346
|
-
kind,
|
|
347
|
-
...(typeof input.title === 'string' ? { title: input.title } : {}),
|
|
348
|
-
...(input.data ? { data: input.data } : {}),
|
|
349
|
-
...(typeof input.x === 'number' ? { x: input.x } : {}),
|
|
350
|
-
...(typeof input.y === 'number' ? { y: input.y } : {}),
|
|
351
|
-
...(typeof input.width === 'number' ? { width: input.width } : {}),
|
|
352
|
-
...(typeof input.height === 'number' ? { height: input.height } : {}),
|
|
353
|
-
...(input.strictSize === true ? { strictSize: true } : {}),
|
|
354
|
-
});
|
|
355
|
-
return {
|
|
356
|
-
content: [{
|
|
357
|
-
type: 'text',
|
|
358
|
-
text: JSON.stringify({
|
|
359
|
-
...(await createdNodePayload(c, result.id, input)),
|
|
360
|
-
primitive: { kind: result.kind, title: result.title, htmlBytes: result.htmlBytes },
|
|
361
|
-
}, null, 2),
|
|
362
|
-
}],
|
|
363
|
-
};
|
|
364
|
-
},
|
|
365
|
-
);
|
|
366
|
-
|
|
367
|
-
// canvas_open_mcp_app + canvas_add_diagram migrated to the operation registry
|
|
368
|
-
// (plan-008 Wave 4): src/server/operations/ops/app.ts (mcpapp.open /
|
|
369
|
-
// diagram.open). Folded into the canvas_app composite.
|
|
370
|
-
|
|
371
|
-
server.tool(
|
|
372
|
-
'canvas_refresh_webpage_node',
|
|
373
|
-
'Deprecated: use canvas_node with action "update" and refresh:true. Refresh a webpage node from its persisted URL so the server re-fetches and caches the latest page text and metadata.',
|
|
374
|
-
{
|
|
375
|
-
id: z.string().describe('Webpage node ID to refresh'),
|
|
376
|
-
url: z.string().optional().describe('Optional replacement URL before refresh'),
|
|
377
|
-
},
|
|
378
|
-
async ({ id, url }) => {
|
|
379
|
-
const c = await ensureCanvas();
|
|
380
|
-
const result = await c.refreshWebpageNode(id, url);
|
|
381
|
-
return {
|
|
382
|
-
content: [{ type: 'text', text: JSON.stringify(result) }],
|
|
383
|
-
...(result.ok ? {} : { isError: true }),
|
|
384
|
-
};
|
|
385
|
-
},
|
|
386
|
-
);
|
|
387
|
-
|
|
388
|
-
// canvas_build_web_artifact migrated to the operation registry (plan-008
|
|
389
|
-
// Wave 4): src/server/operations/ops/app.ts (webartifact.build). Folded into
|
|
390
|
-
// the canvas_app composite.
|
|
391
|
-
|
|
392
|
-
// canvas_remove_annotation migrated to the operation registry (plan-008
|
|
393
|
-
// Wave 1): src/server/operations/ops/annotation.ts.
|
|
394
|
-
|
|
395
|
-
// ── AX context and focus ───────────────────────────────────────
|
|
396
|
-
// canvas_get_ax + canvas_set_ax_focus migrated to the operation registry
|
|
397
|
-
// (plan-007 Slice B.1): src/server/operations/ops/ax-state.ts.
|
|
398
|
-
|
|
399
|
-
// canvas_record_ax_event / canvas_send_steering / canvas_get_ax_timeline
|
|
400
|
-
// migrated to the operation registry (plan-007 Slice B wave 3):
|
|
401
|
-
// src/server/operations/ops/ax-timeline.ts.
|
|
402
|
-
|
|
403
|
-
// canvas_add_work_item / canvas_update_work_item / canvas_request_approval /
|
|
404
|
-
// canvas_resolve_approval migrated to the operation registry (plan-007 Slice B
|
|
405
|
-
// wave 2): src/server/operations/ops/ax-work.ts.
|
|
406
|
-
|
|
407
|
-
// canvas_add_evidence migrated to the operation registry (plan-007 Slice B
|
|
408
|
-
// wave 3): src/server/operations/ops/ax-timeline.ts.
|
|
409
|
-
|
|
410
|
-
// canvas_add_review_annotation migrated to the operation registry (plan-007
|
|
411
|
-
// Slice B wave 2): src/server/operations/ops/ax-work.ts.
|
|
412
|
-
|
|
413
|
-
// canvas_report_host_capability migrated to the operation registry
|
|
414
|
-
// (plan-007 Slice B.1): src/server/operations/ops/ax-state.ts.
|
|
415
|
-
|
|
416
289
|
server.tool(
|
|
417
290
|
'canvas_ax_interaction',
|
|
418
291
|
'Submit a node-originated AX interaction: a capability-gated, validated event from an eligible node that maps onto an AX operation (work item, evidence, approval, review, focus, steering, event). Returns { ok: false, code } if the node type/metadata does not allow the interaction type or the payload is invalid.',
|
|
419
292
|
{
|
|
420
|
-
type: z
|
|
293
|
+
type: z
|
|
294
|
+
.enum(AX_INTERACTION_TYPES)
|
|
295
|
+
.describe('Interaction type, e.g. ax.work.create, ax.evidence.add, ax.focus.set.'),
|
|
421
296
|
sourceNodeId: z.string().describe('The node emitting the interaction.'),
|
|
422
|
-
payload: z
|
|
297
|
+
payload: z
|
|
298
|
+
.record(z.string(), z.unknown())
|
|
299
|
+
.optional()
|
|
300
|
+
.describe('Type-specific payload, e.g. {"title":"..."} for ax.work.create.'),
|
|
423
301
|
sourceSurface: z.enum(['native-node', 'json-render', 'html-node', 'mcp-app', 'adapter']).optional(),
|
|
424
302
|
correlationId: z.string().optional(),
|
|
425
|
-
source: z
|
|
303
|
+
source: z
|
|
304
|
+
.enum(['agent', 'api', 'browser', 'cli', 'codex', 'copilot', 'mcp', 'sdk', 'system'])
|
|
426
305
|
.optional()
|
|
427
306
|
.describe('Optional host/source label. Defaults to mcp.'),
|
|
428
307
|
},
|
|
429
308
|
async ({ type, sourceNodeId, payload, sourceSurface, correlationId, source }) => {
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
309
|
+
try {
|
|
310
|
+
const c = await ensureCanvas();
|
|
311
|
+
const result = await c.submitAxInteraction(
|
|
312
|
+
{
|
|
313
|
+
type,
|
|
314
|
+
sourceNodeId,
|
|
315
|
+
...(payload ? { payload } : {}),
|
|
316
|
+
...(sourceSurface ? { sourceSurface } : {}),
|
|
317
|
+
...(correlationId ? { correlationId } : {}),
|
|
318
|
+
},
|
|
319
|
+
{ source: source ?? 'mcp' },
|
|
320
|
+
);
|
|
321
|
+
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
322
|
+
} catch (error) {
|
|
323
|
+
return {
|
|
324
|
+
content: [{ type: 'text', text: error instanceof Error ? error.message : String(error) }],
|
|
325
|
+
isError: true,
|
|
326
|
+
};
|
|
327
|
+
}
|
|
442
328
|
},
|
|
443
329
|
);
|
|
444
330
|
|
|
@@ -453,47 +339,84 @@ export async function startMcpServer(): Promise<void> {
|
|
|
453
339
|
'canvas_ingest_activity',
|
|
454
340
|
'Ingest a normalized agent activity (a tool/session event your harness forwards) so the board reacts automatically — primitive A, makes AX bidirectional. Always records a timeline event; kind-driven default reactions (overridable per call via `reactions`): failure/error → work item (blocked) + review finding + evidence (logs); tool-result + outcome:"success" → evidence (tool-result); everything else (tool-start, session-*, command, note) → event only. Set any reaction to false to suppress it, or to an object to override its fields. Returns { event, workItem, evidence, review }.',
|
|
455
341
|
{
|
|
456
|
-
kind: z.enum([
|
|
342
|
+
kind: z.enum([
|
|
343
|
+
'tool-start',
|
|
344
|
+
'tool-result',
|
|
345
|
+
'failure',
|
|
346
|
+
'error',
|
|
347
|
+
'session-start',
|
|
348
|
+
'session-end',
|
|
349
|
+
'command',
|
|
350
|
+
'note',
|
|
351
|
+
]),
|
|
457
352
|
title: z.string(),
|
|
458
353
|
summary: z.string().optional(),
|
|
459
354
|
outcome: z.enum(['success', 'failure']).optional(),
|
|
460
|
-
ref: z
|
|
355
|
+
ref: z
|
|
356
|
+
.string()
|
|
357
|
+
.optional()
|
|
358
|
+
.describe('A file path, URL, or commit the activity refers to (used as the review file anchor for failures).'),
|
|
461
359
|
nodeIds: z.array(z.string()).optional(),
|
|
462
360
|
data: z.record(z.string(), z.unknown()).optional(),
|
|
463
|
-
reactions: z
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
361
|
+
reactions: z
|
|
362
|
+
.object({
|
|
363
|
+
workItem: z
|
|
364
|
+
.union([
|
|
365
|
+
z.literal(false),
|
|
366
|
+
z.object({
|
|
367
|
+
status: z.enum(['todo', 'in-progress', 'blocked', 'done', 'cancelled']).optional(),
|
|
368
|
+
detail: z.string().nullable().optional(),
|
|
369
|
+
}),
|
|
370
|
+
])
|
|
371
|
+
.optional(),
|
|
372
|
+
evidence: z
|
|
373
|
+
.union([
|
|
374
|
+
z.literal(false),
|
|
375
|
+
z.object({
|
|
376
|
+
kind: z.enum(['logs', 'tool-result', 'screenshot', 'file', 'diff', 'test-output']).optional(),
|
|
377
|
+
body: z.string().nullable().optional(),
|
|
378
|
+
}),
|
|
379
|
+
])
|
|
380
|
+
.optional(),
|
|
381
|
+
review: z
|
|
382
|
+
.union([
|
|
383
|
+
z.literal(false),
|
|
384
|
+
z.object({
|
|
385
|
+
severity: z.enum(['info', 'warning', 'error']).optional(),
|
|
386
|
+
kind: z.enum(['comment', 'finding']).optional(),
|
|
387
|
+
anchorType: z.enum(['node', 'file', 'region']).optional(),
|
|
388
|
+
nodeId: z.string().nullable().optional(),
|
|
389
|
+
}),
|
|
390
|
+
])
|
|
391
|
+
.optional(),
|
|
392
|
+
})
|
|
393
|
+
.optional()
|
|
394
|
+
.describe('Override or suppress the kind-driven default reactions.'),
|
|
479
395
|
source: z.enum(['agent', 'api', 'browser', 'cli', 'codex', 'copilot', 'mcp', 'sdk', 'system']).optional(),
|
|
480
396
|
},
|
|
481
397
|
async ({ kind, title, summary, outcome, ref, nodeIds, data, reactions, source }) => {
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
398
|
+
try {
|
|
399
|
+
const c = await ensureCanvas();
|
|
400
|
+
const result = await c.ingestActivity(
|
|
401
|
+
{
|
|
402
|
+
kind,
|
|
403
|
+
title,
|
|
404
|
+
...(summary !== undefined ? { summary } : {}),
|
|
405
|
+
...(outcome !== undefined ? { outcome } : {}),
|
|
406
|
+
...(ref !== undefined ? { ref } : {}),
|
|
407
|
+
...(nodeIds !== undefined ? { nodeIds } : {}),
|
|
408
|
+
...(data !== undefined ? { data } : {}),
|
|
409
|
+
...(reactions !== undefined ? { reactions } : {}),
|
|
410
|
+
},
|
|
411
|
+
{ source: source ?? 'mcp' },
|
|
412
|
+
);
|
|
413
|
+
return { content: [{ type: 'text', text: JSON.stringify({ ok: true, ...result }) }] };
|
|
414
|
+
} catch (error) {
|
|
415
|
+
return {
|
|
416
|
+
content: [{ type: 'text', text: error instanceof Error ? error.message : String(error) }],
|
|
417
|
+
isError: true,
|
|
418
|
+
};
|
|
419
|
+
}
|
|
497
420
|
},
|
|
498
421
|
);
|
|
499
422
|
|
|
@@ -516,9 +439,12 @@ export async function startMcpServer(): Promise<void> {
|
|
|
516
439
|
// ── canvas_screenshot ─────────────────────────────────────────
|
|
517
440
|
server.tool(
|
|
518
441
|
'canvas_screenshot',
|
|
519
|
-
'Capture a screenshot from the active Bun.WebView automation session. Returns both an MCP image payload and JSON metadata. Requires an active automation session started via
|
|
442
|
+
'Capture a screenshot from the active Bun.WebView automation session. Returns both an MCP image payload and JSON metadata. Requires an active automation session started via canvas_webview with action "start".',
|
|
520
443
|
{
|
|
521
|
-
format: z
|
|
444
|
+
format: z
|
|
445
|
+
.enum(['png', 'jpeg', 'webp'])
|
|
446
|
+
.optional()
|
|
447
|
+
.describe('Screenshot format (default depends on Bun; png recommended)'),
|
|
522
448
|
quality: z.number().optional().describe('Optional quality for lossy formats'),
|
|
523
449
|
},
|
|
524
450
|
async ({ format, quality }) => {
|
|
@@ -534,27 +460,21 @@ export async function startMcpServer(): Promise<void> {
|
|
|
534
460
|
{
|
|
535
461
|
type: 'image',
|
|
536
462
|
data: encodeBase64(bytes),
|
|
537
|
-
mimeType:
|
|
538
|
-
format === 'jpeg'
|
|
539
|
-
? 'image/jpeg'
|
|
540
|
-
: format === 'webp'
|
|
541
|
-
? 'image/webp'
|
|
542
|
-
: 'image/png',
|
|
463
|
+
mimeType: format === 'jpeg' ? 'image/jpeg' : format === 'webp' ? 'image/webp' : 'image/png',
|
|
543
464
|
},
|
|
544
465
|
{
|
|
545
466
|
type: 'text',
|
|
546
|
-
text: JSON.stringify(
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
format === 'jpeg'
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
}, null, 2),
|
|
467
|
+
text: JSON.stringify(
|
|
468
|
+
{
|
|
469
|
+
bytes: bytes.byteLength,
|
|
470
|
+
backend: status.backend,
|
|
471
|
+
width: status.width,
|
|
472
|
+
height: status.height,
|
|
473
|
+
mimeType: format === 'jpeg' ? 'image/jpeg' : format === 'webp' ? 'image/webp' : 'image/png',
|
|
474
|
+
},
|
|
475
|
+
null,
|
|
476
|
+
2,
|
|
477
|
+
),
|
|
558
478
|
},
|
|
559
479
|
],
|
|
560
480
|
};
|
|
@@ -576,8 +496,7 @@ export async function startMcpServer(): Promise<void> {
|
|
|
576
496
|
'schema',
|
|
577
497
|
'canvas://schema',
|
|
578
498
|
{
|
|
579
|
-
description:
|
|
580
|
-
`Machine-readable create schemas, canonical examples, json-render catalog details, and MCP node-type routing from the running PMX Canvas server version. Routing: ${structuredSchemaDescription()}.`,
|
|
499
|
+
description: `Machine-readable create schemas, canonical examples, json-render catalog details, and MCP node-type routing from the running PMX Canvas server version. Routing: ${structuredSchemaDescription()}.`,
|
|
581
500
|
mimeType: 'application/json',
|
|
582
501
|
},
|
|
583
502
|
async () => ({
|
|
@@ -607,20 +526,20 @@ export async function startMcpServer(): Promise<void> {
|
|
|
607
526
|
const layout = await c.getLayout();
|
|
608
527
|
|
|
609
528
|
const pinnedNodes = layout.nodes.filter((n) => pinnedIds.has(n.id));
|
|
610
|
-
const pinnedEdges = layout.edges.filter(
|
|
611
|
-
(e) => pinnedIds.has(e.from) && pinnedIds.has(e.to),
|
|
612
|
-
);
|
|
529
|
+
const pinnedEdges = layout.edges.filter((e) => pinnedIds.has(e.from) && pinnedIds.has(e.to));
|
|
613
530
|
|
|
614
531
|
// Compute neighborhoods: for each pinned node, find nearby unpinned nodes
|
|
615
532
|
const neighborhoods = findNeighborhoods(layout.nodes, pinnedIds);
|
|
616
533
|
|
|
617
534
|
const context = {
|
|
618
535
|
pinnedCount: pinnedNodes.length,
|
|
619
|
-
nodes: pinnedNodes.map((n) =>
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
536
|
+
nodes: pinnedNodes.map((n) =>
|
|
537
|
+
serializeNodeForAgentContext(n, {
|
|
538
|
+
defaultTextLength: 700,
|
|
539
|
+
webpageTextLength: 1600,
|
|
540
|
+
includePosition: true,
|
|
541
|
+
}),
|
|
542
|
+
),
|
|
624
543
|
edges: pinnedEdges.map((e) => ({
|
|
625
544
|
id: e.id,
|
|
626
545
|
from: e.from,
|
|
@@ -674,8 +593,7 @@ export async function startMcpServer(): Promise<void> {
|
|
|
674
593
|
'ax-context',
|
|
675
594
|
'canvas://ax-context',
|
|
676
595
|
{
|
|
677
|
-
description:
|
|
678
|
-
'Agent-ready PMX AX context combining pinned context, focus, and surface metadata.',
|
|
596
|
+
description: 'Agent-ready PMX AX context combining pinned context, focus, and surface metadata.',
|
|
679
597
|
mimeType: 'application/json',
|
|
680
598
|
},
|
|
681
599
|
async () => {
|
|
@@ -733,14 +651,18 @@ export async function startMcpServer(): Promise<void> {
|
|
|
733
651
|
{
|
|
734
652
|
uri: 'canvas://ax-work',
|
|
735
653
|
mimeType: 'application/json',
|
|
736
|
-
text: JSON.stringify(
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
654
|
+
text: JSON.stringify(
|
|
655
|
+
{
|
|
656
|
+
workItems,
|
|
657
|
+
approvalGates,
|
|
658
|
+
reviewAnnotations: state.reviewAnnotations,
|
|
659
|
+
elicitations: state.elicitations,
|
|
660
|
+
modeRequests: state.modeRequests,
|
|
661
|
+
policy: state.policy,
|
|
662
|
+
},
|
|
663
|
+
null,
|
|
664
|
+
2,
|
|
665
|
+
),
|
|
744
666
|
},
|
|
745
667
|
],
|
|
746
668
|
};
|
|
@@ -752,7 +674,7 @@ export async function startMcpServer(): Promise<void> {
|
|
|
752
674
|
'canvas://ax-pending-steering',
|
|
753
675
|
{
|
|
754
676
|
description:
|
|
755
|
-
'Adapterless AX delivery surface. `pending`: undelivered steering messages to claim and act on, then mark via
|
|
677
|
+
'Adapterless AX delivery surface. `pending`: undelivered steering messages to claim and act on, then mark via canvas_ax_delivery { action: "mark" }. `pendingActivity`: open canvas-bound AX items awaiting the agent (open work items, pending approval gates / elicitations / mode requests) — usually created by the human in the browser; these fire ax-state-changed (resource-subscribers are also pushed canvas://ax-work). Resolve pendingActivity via its own tool, not the delivery mark action. Use canvas_ax_delivery { action: "claim" } for the loop-safe, consumer-scoped view.',
|
|
756
678
|
mimeType: 'application/json',
|
|
757
679
|
},
|
|
758
680
|
async () => {
|
|
@@ -761,7 +683,11 @@ export async function startMcpServer(): Promise<void> {
|
|
|
761
683
|
const pendingActivity = buildPendingAxActivity(state);
|
|
762
684
|
return {
|
|
763
685
|
contents: [
|
|
764
|
-
{
|
|
686
|
+
{
|
|
687
|
+
uri: 'canvas://ax-pending-steering',
|
|
688
|
+
mimeType: 'application/json',
|
|
689
|
+
text: JSON.stringify({ pending, pendingActivity }, null, 2),
|
|
690
|
+
},
|
|
765
691
|
],
|
|
766
692
|
};
|
|
767
693
|
},
|
|
@@ -780,7 +706,11 @@ export async function startMcpServer(): Promise<void> {
|
|
|
780
706
|
const timeline = await c.getAxTimeline();
|
|
781
707
|
return {
|
|
782
708
|
contents: [
|
|
783
|
-
{
|
|
709
|
+
{
|
|
710
|
+
uri: 'canvas://ax-delivery',
|
|
711
|
+
mimeType: 'application/json',
|
|
712
|
+
text: JSON.stringify({ steering: timeline.steering }, null, 2),
|
|
713
|
+
},
|
|
784
714
|
],
|
|
785
715
|
};
|
|
786
716
|
},
|
|
@@ -859,13 +789,18 @@ export async function startMcpServer(): Promise<void> {
|
|
|
859
789
|
'grouped together), provides reading order (top-left to bottom-right), and shows ' +
|
|
860
790
|
'neighborhoods around pinned nodes (nearby unpinned nodes the human implicitly associated). ' +
|
|
861
791
|
'This makes "spatial arrangement is communication" real — read this to understand the ' +
|
|
862
|
-
|
|
792
|
+
"human's spatial intent, not just which nodes are pinned.",
|
|
863
793
|
mimeType: 'application/json',
|
|
864
794
|
},
|
|
865
795
|
async () => {
|
|
866
796
|
const c = await ensureCanvas();
|
|
867
797
|
const layout = await c.getLayout();
|
|
868
|
-
const spatial = buildSpatialContext(
|
|
798
|
+
const spatial = buildSpatialContext(
|
|
799
|
+
layout.nodes,
|
|
800
|
+
layout.edges,
|
|
801
|
+
new Set(await c.getPinnedNodeIds()),
|
|
802
|
+
layout.annotations ?? [],
|
|
803
|
+
);
|
|
869
804
|
return {
|
|
870
805
|
contents: [
|
|
871
806
|
{
|
|
@@ -931,8 +866,8 @@ export async function startMcpServer(): Promise<void> {
|
|
|
931
866
|
// ── canvas://skills ────────────────────────────────────────
|
|
932
867
|
// Discoverability for the skill prompts bundled with the npm package
|
|
933
868
|
// (skills/<name>/SKILL.md). Before 0.1.2 these files shipped but were
|
|
934
|
-
// invisible to agents —
|
|
935
|
-
//
|
|
869
|
+
// invisible to agents — building web artifacts without the companion
|
|
870
|
+
// `web-artifacts-builder` skill led to predictable misuse.
|
|
936
871
|
// The index lists every bundled skill with its frontmatter description;
|
|
937
872
|
// individual skills are served verbatim at canvas://skills/<name>.
|
|
938
873
|
server.resource(
|
|
@@ -943,7 +878,7 @@ export async function startMcpServer(): Promise<void> {
|
|
|
943
878
|
'Index of agent skills bundled with this PMX Canvas install. Lists name, ' +
|
|
944
879
|
'description, and per-skill URI (canvas://skills/<name>). Read a specific ' +
|
|
945
880
|
'skill for workflow guidance — notably web-artifacts-builder for ' +
|
|
946
|
-
'
|
|
881
|
+
'canvas_app { action: "build-artifact" }, and pmx-canvas for the broader workbench.',
|
|
947
882
|
mimeType: 'application/json',
|
|
948
883
|
},
|
|
949
884
|
async () => {
|