pmx-canvas 0.9.3 → 0.10.0

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.
Files changed (56) hide show
  1. package/.github/extensions/pmx-canvas/runtime.json +1 -1
  2. package/.github/extensions/pmx-canvas/tools.json +9278 -2014
  3. package/CHANGELOG.md +22 -1
  4. package/dist/canvas/index.js +1 -1
  5. package/dist/canvas/mcp-app.html +1 -1
  6. package/dist/types/mcp/canvas-access.d.ts +1 -12
  7. package/dist/types/mcp/resource-subscriptions.d.ts +7 -0
  8. package/dist/types/mcp/server.d.ts +2 -0
  9. package/dist/types/server/ax-wait.d.ts +1 -0
  10. package/dist/types/server/canvas-operations.d.ts +1 -0
  11. package/dist/types/server/index.d.ts +3 -3
  12. package/dist/types/server/operations/http.d.ts +10 -0
  13. package/dist/types/server/operations/index.d.ts +2 -2
  14. package/dist/types/server/operations/invoker.d.ts +4 -3
  15. package/dist/types/server/operations/mcp-contracts.d.ts +20 -0
  16. package/dist/types/server/operations/mcp.d.ts +2 -0
  17. package/dist/types/server/operations/ops/batch.d.ts +1 -0
  18. package/dist/types/server/operations/registry.d.ts +2 -2
  19. package/dist/types/server/operations/types.d.ts +12 -2
  20. package/dist/types/server/server.d.ts +2 -0
  21. package/dist/types/server/webpage-node.d.ts +1 -0
  22. package/docs/api-reference.md +2 -2
  23. package/docs/cli.md +1 -1
  24. package/docs/http-api.md +31 -0
  25. package/docs/mcp.md +93 -21
  26. package/docs/plans/plan-019-post-0.9-roadmap.md +123 -6
  27. package/docs/sdk.md +5 -2
  28. package/package.json +1 -1
  29. package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/runtime.json +1 -1
  30. package/plugins/pmx-canvas/plugin.json +1 -1
  31. package/plugins/pmx-canvas/runtime.json +1 -1
  32. package/plugins/pmx-canvas/tools.json +9278 -2014
  33. package/skills/pmx-canvas/SKILL.md +9 -4
  34. package/skills/pmx-canvas/references/full-reference.md +35 -2
  35. package/skills/pmx-canvas/references/installing-pmx-canvas.md +7 -7
  36. package/src/client/canvas/ToolRail.tsx +2 -0
  37. package/src/mcp/canvas-access.ts +6 -47
  38. package/src/mcp/resource-subscriptions.ts +181 -0
  39. package/src/mcp/server.ts +185 -219
  40. package/src/server/ax-wait.ts +21 -6
  41. package/src/server/canvas-operations.ts +6 -2
  42. package/src/server/index.ts +12 -4
  43. package/src/server/operations/http.ts +69 -21
  44. package/src/server/operations/index.ts +8 -1
  45. package/src/server/operations/invoker.ts +53 -5
  46. package/src/server/operations/mcp-contracts.ts +339 -0
  47. package/src/server/operations/mcp.ts +153 -18
  48. package/src/server/operations/ops/ax-await.ts +18 -10
  49. package/src/server/operations/ops/ax-read.ts +2 -0
  50. package/src/server/operations/ops/ax-timeline.ts +2 -2
  51. package/src/server/operations/ops/batch.ts +27 -8
  52. package/src/server/operations/ops/nodes.ts +18 -3
  53. package/src/server/operations/registry.ts +24 -4
  54. package/src/server/operations/types.ts +16 -2
  55. package/src/server/server.ts +10 -0
  56. package/src/server/webpage-node.ts +9 -4
@@ -1,16 +1,11 @@
1
1
  import { type CanvasLayout, type CanvasNodeState, type PmxCanvas } from '../server/index.js';
2
- import type { PmxAxSource } from '../server/ax-state.js';
3
2
  import type { ContextReadInput } from '../server/context-reads.js';
4
3
  import { type OperationInvoker } from '../server/operations/index.js';
5
4
  type AxStateResult = ReturnType<PmxCanvas['getAxState']>;
6
5
  type AxContextResult = ReturnType<PmxCanvas['getAxContext']>;
7
- type SubmitAxInteractionInput = Parameters<PmxCanvas['submitAxInteraction']>[0];
8
- type SubmitAxInteractionResult = ReturnType<PmxCanvas['submitAxInteraction']>;
9
6
  type GetPendingSteeringResult = ReturnType<PmxCanvas['getPendingSteering']>;
10
7
  type ListElicitationsResult = ReturnType<PmxCanvas['listElicitations']>;
11
8
  type ListModeRequestsResult = ReturnType<PmxCanvas['listModeRequests']>;
12
- type IngestActivityInput = Parameters<PmxCanvas['ingestActivity']>[0];
13
- type IngestActivityResult = ReturnType<PmxCanvas['ingestActivity']>;
14
9
  type GetPolicyResult = ReturnType<PmxCanvas['getPolicy']>;
15
10
  type GetAxTimelineQuery = Parameters<PmxCanvas['getAxTimeline']>[0];
16
11
  type GetAxTimelineResult = ReturnType<PmxCanvas['getAxTimeline']>;
@@ -38,18 +33,12 @@ export interface CanvasAccess {
38
33
  listWorkItems(): Promise<ListWorkItemsResult>;
39
34
  listApprovalGates(): Promise<ListApprovalGatesResult>;
40
35
  listReviewAnnotations(): Promise<ListReviewAnnotationsResult>;
41
- submitAxInteraction(input: SubmitAxInteractionInput, options?: {
42
- source?: PmxAxSource;
43
- }): Promise<SubmitAxInteractionResult>;
44
36
  getPendingSteering(options?: {
45
37
  consumer?: string;
46
38
  limit?: number;
47
39
  }): Promise<GetPendingSteeringResult>;
48
40
  listElicitations(): Promise<ListElicitationsResult>;
49
41
  listModeRequests(): Promise<ListModeRequestsResult>;
50
- ingestActivity(input: IngestActivityInput, options?: {
51
- source?: PmxAxSource;
52
- }): Promise<IngestActivityResult>;
53
42
  getPolicy(): Promise<GetPolicyResult>;
54
43
  getHistory(): Promise<HistoryResult>;
55
44
  getPinnedNodeIds(board?: string): Promise<string[]>;
@@ -62,7 +51,7 @@ export interface CanvasAccess {
62
51
  runBatch(operations: RunBatchInput): Promise<RunBatchResult>;
63
52
  getCodeGraph(): Promise<CodeGraphResult>;
64
53
  getAutomationWebViewStatus(): Promise<AutomationWebViewStatus>;
65
- screenshotAutomationWebView(options?: AutomationScreenshotOptions): Promise<Uint8Array>;
54
+ screenshotAutomationWebView(options?: AutomationScreenshotOptions, signal?: AbortSignal): Promise<Uint8Array>;
66
55
  }
67
56
  export declare function refreshCanvasAccess(access: CanvasAccess): Promise<CanvasAccess>;
68
57
  /**
@@ -0,0 +1,7 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { CanvasAccess } from './canvas-access.js';
3
+ /** One subscription set and event source per MCP connection. Notifications are not reads. */
4
+ export declare function registerResourceSubscriptions(server: McpServer, getCanvas: () => Promise<CanvasAccess>): {
5
+ attach: (canvas: CanvasAccess) => void;
6
+ dispose: () => void;
7
+ };
@@ -19,4 +19,6 @@
19
19
  * }
20
20
  * ```
21
21
  */
22
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
23
+ export declare function createMcpServer(): McpServer;
22
24
  export declare function startMcpServer(): Promise<void>;
@@ -33,4 +33,5 @@ export declare function waitForAxCondition(opts: {
33
33
  ready: () => boolean;
34
34
  timeoutMs: number;
35
35
  channel?: string;
36
+ signal?: AbortSignal;
36
37
  }): Promise<void>;
@@ -168,6 +168,7 @@ export declare function resolveCanvasNode(nodeRef: CanvasNodeLookupInput): {
168
168
  };
169
169
  export declare function refreshCanvasWebpageNode(id: string, options?: {
170
170
  url?: string;
171
+ signal?: AbortSignal;
171
172
  }): Promise<{
172
173
  ok: boolean;
173
174
  id: string;
@@ -1,8 +1,8 @@
1
1
  import { EventEmitter } from 'node:events';
2
2
  import type { Camera, Tour, TourStop } from '../shared/tour.js';
3
- import { type TourStep, type TourStepResult } from './tour-control.js';
3
+ import { type TourState, type TourStep, type TourStepResult } from './tour-control.js';
4
4
  export type { Tour, TourStop } from '../shared/tour.js';
5
- export type { TourStep, TourStepResult } from './tour-control.js';
5
+ export type { TourState, TourStep, TourStepResult } from './tour-control.js';
6
6
  import type { AgentPhase, AgentPresence, AgentPresenceSnapshot } from '../shared/agent-presence.js';
7
7
  import { canvasState } from './canvas-state.js';
8
8
  import type { CanvasAnnotation, CanvasNodeState, CanvasEdge, CanvasLayout } from './canvas-state.js';
@@ -163,7 +163,7 @@ export declare class PmxCanvas extends EventEmitter {
163
163
  ungroupNodes(groupId: string, options?: {
164
164
  intentId?: string;
165
165
  }): boolean;
166
- getTour(): Tour;
166
+ getTour(): TourState;
167
167
  setTour(tour: Tour | null): void;
168
168
  /** Present a tour stop (index, `next` or `previous`) in the workbench. Not recorded in undo history. */
169
169
  goToTourStop(step: TourStep, options?: {
@@ -1,3 +1,13 @@
1
+ import { type OperationProgress } from './types.js';
2
+ /** Opt-in NDJSON; the terminal frame retains the ordinary HTTP status/body. */
3
+ export type OperationStreamFrame = {
4
+ type: 'progress';
5
+ progress: OperationProgress;
6
+ } | {
7
+ type: 'result';
8
+ status: number;
9
+ body: unknown;
10
+ };
1
11
  export declare function matchOperationPath(template: string, pathname: string): Record<string, string> | null;
2
12
  /**
3
13
  * Shared body reader: preserves the parsed JSON value as-is (object, array,
@@ -3,6 +3,6 @@ export { dispatchOperationRoute } from './http.js';
3
3
  export { runCanvasBatchOperation, type BatchEnvelope } from './ops/batch.js';
4
4
  export type { OpenMcpAppCoreResult } from './ops/app.js';
5
5
  export { LocalOperationInvoker, HttpOperationInvoker, type OperationInvoker } from './invoker.js';
6
- export { registerOperationTools, registerCompositeTools, type OperationToolHost } from './mcp.js';
6
+ export { registerOperationTools, registerCompositeTools, registerOperationHelpTool, type OperationToolHost, } from './mcp.js';
7
7
  export { compositeToolDefinitions, type CompositeToolDefinition } from './composites.js';
8
- export { OperationError, defineOperation, type Operation, type OperationContext, type OperationDefinition, type OperationErrorStatus, } from './types.js';
8
+ export { OperationError, defineOperation, type Operation, type OperationContext, type OperationDefinition, type OperationExecutionOptions, type OperationErrorStatus, type OperationProgress, } from './types.js';
@@ -1,5 +1,6 @@
1
+ import { type OperationExecutionOptions } from './types.js';
1
2
  export interface OperationInvoker {
2
- invoke(name: string, input: Record<string, unknown>): Promise<unknown>;
3
+ invoke(name: string, input: Record<string, unknown>, options?: OperationExecutionOptions): Promise<unknown>;
3
4
  }
4
5
  /**
5
6
  * The agent-presence label for a transport: `PMX_CANVAS_AGENT_SOURCE` lets a
@@ -14,7 +15,7 @@ export declare class LocalOperationInvoker implements OperationInvoker {
14
15
  private readonly proxiedRead;
15
16
  /** `source` labels this caller's agent presence ('mcp', 'sdk', …). */
16
17
  constructor(source?: string, allowExternalMcp?: boolean);
17
- invoke(name: string, input: Record<string, unknown>): Promise<unknown>;
18
+ invoke(name: string, input: Record<string, unknown>, options?: OperationExecutionOptions): Promise<unknown>;
18
19
  }
19
20
  /** Builds the HTTP request from the op's route template (`:id` from input, GET flags to query). */
20
21
  export declare class HttpOperationInvoker implements OperationInvoker {
@@ -24,5 +25,5 @@ export declare class HttpOperationInvoker implements OperationInvoker {
24
25
  private readonly proxiedRead;
25
26
  /** `source` labels this caller's agent presence on the server ('cli', 'mcp', …). */
26
27
  constructor(baseUrl: string, source?: string, allowExternalMcp?: boolean);
27
- invoke(name: string, rawInput: Record<string, unknown>): Promise<unknown>;
28
+ invoke(name: string, rawInput: Record<string, unknown>, options?: OperationExecutionOptions): Promise<unknown>;
28
29
  }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * MCP's output is the FORMATTER projection, never the larger HTTP payload.
3
+ * `result` wraps prose/arrays without pretending they are JSON objects. Each
4
+ * operation validates its stable fields; loose payloads preserve full:true and
5
+ * extensible node/app data. No schema advertises an unconstrained root record.
6
+ */
7
+ import { z } from 'zod';
8
+ import type { CallToolResult, ToolAnnotations } from '@modelcontextprotocol/sdk/types.js';
9
+ export declare function operationOutputSchema(operations: string[]): z.ZodObject<{
10
+ result: z.ZodUnion<readonly [...z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>[], z.ZodObject<{
11
+ operation: z.ZodNull;
12
+ outcome: z.ZodLiteral<"error">;
13
+ data: z.ZodObject<{
14
+ message: z.ZodString;
15
+ }, z.core.$strict>;
16
+ }, z.core.$strict>]>;
17
+ }, z.core.$strict>;
18
+ export declare function structuredOperationResult(operation: string, formatted: CallToolResult): CallToolResult;
19
+ export declare function operationToolError(error: unknown): CallToolResult;
20
+ export declare function operationToolAnnotations(toolName: string): ToolAnnotations;
@@ -12,6 +12,8 @@ export interface OperationToolHost extends OperationMcpToolHost {
12
12
  recordContextRead(read: ContextReadInput): Promise<void>;
13
13
  }
14
14
  export declare function registerOperationTools(server: McpServer, getHost: () => Promise<OperationToolHost>): void;
15
+ /** Registry metadata only: never boots a canvas or records a content read. */
16
+ export declare function registerOperationHelpTool(server: McpServer): void;
15
17
  /**
16
18
  * Register composite (action-discriminated) MCP tools (plan-006). Each action
17
19
  * dispatches to a registered operation, reusing that op's `mcp.buildInput` and
@@ -5,6 +5,7 @@ export interface BatchEnvelope {
5
5
  refs: Record<string, unknown>;
6
6
  failedIndex?: number;
7
7
  error?: string;
8
+ cancelled?: boolean;
8
9
  }
9
10
  export declare const batchOperations: Operation[];
10
11
  /**
@@ -1,4 +1,4 @@
1
- import { type Operation } from './types.js';
1
+ import { type Operation, type OperationExecutionOptions } from './types.js';
2
2
  export declare function registerOperation(op: Operation): void;
3
3
  export declare function getOperation(name: string): Operation;
4
4
  export declare function listOperations(): Operation[];
@@ -12,7 +12,7 @@ export declare function getOperationEventEmitter(): OperationEventEmitter | null
12
12
  export declare function isEmitSuppressed(): boolean;
13
13
  /** Suppress this call chain's per-entry frames; the batch emits its final frame afterward. */
14
14
  export declare function runWithSuppressedEmits<T>(fn: () => Promise<T>): Promise<T>;
15
- export interface ExecuteOperationMeta {
15
+ export interface ExecuteOperationMeta extends OperationExecutionOptions {
16
16
  /**
17
17
  * Skip the synthesized auto-ghost: set by the workbench's own HTTP calls
18
18
  * (a human dragging a node is not agent activity) and by canvas.batch for
@@ -20,7 +20,17 @@ export declare class OperationError extends Error {
20
20
  readonly details?: Record<string, unknown>;
21
21
  constructor(message: string, status?: OperationErrorStatus, details?: Record<string, unknown>);
22
22
  }
23
- export interface OperationContext {
23
+ export interface OperationProgress {
24
+ progress: number;
25
+ total?: number;
26
+ message?: string;
27
+ }
28
+ /** Cooperative cancellation: completed writes are never rolled back. */
29
+ export interface OperationExecutionOptions {
30
+ signal?: AbortSignal;
31
+ onProgress?: (progress: OperationProgress) => void | Promise<void>;
32
+ }
33
+ export interface OperationContext extends OperationExecutionOptions {
24
34
  /** Emit a workbench SSE event (e.g. extra `canvas-layout-update` frames, focus, viewport). */
25
35
  emit(event: string, payload?: Record<string, unknown>): void;
26
36
  }
@@ -53,7 +63,7 @@ export interface OperationMcpToolHost {
53
63
  * restore summary).
54
64
  */
55
65
  invoker(): {
56
- invoke(name: string, input: Record<string, unknown>): Promise<unknown>;
66
+ invoke(name: string, input: Record<string, unknown>, options?: OperationExecutionOptions): Promise<unknown>;
57
67
  };
58
68
  }
59
69
  export interface OperationMcpTool {
@@ -80,6 +80,8 @@ export interface PrimaryWorkbenchCanvasPromptRequest {
80
80
  contextNodeIds: string[];
81
81
  }
82
82
  type PrimaryWorkbenchCanvasPromptHandler = (request: PrimaryWorkbenchCanvasPromptRequest) => Promise<void>;
83
+ /** In-process invalidation observers share the same events as daemon SSE clients. */
84
+ export declare function onWorkbenchEvent(listener: (event: string) => void): () => void;
83
85
  export declare function setPrimaryWorkbenchAutoOpenEnabled(enabled: boolean): void;
84
86
  export declare function isPrimaryWorkbenchAutoOpenEnabled(): boolean;
85
87
  export declare function hasWorkbenchSubscribers(): boolean;
@@ -37,6 +37,7 @@ export declare function checkWebpageHop(originScope: AddressScope, hopScope: Add
37
37
  export declare function fetchWebpageSnapshot(inputUrl: string, options?: {
38
38
  resolveHost?: ResolveHost;
39
39
  timeoutMs?: number;
40
+ signal?: AbortSignal;
40
41
  }): Promise<WebpageSnapshot>;
41
42
  export declare function getWebpageFetchErrorDetails(error: unknown): {
42
43
  message: string;
@@ -6,7 +6,7 @@
6
6
 
7
7
  ## MCP Server
8
8
 
9
- 24 tools: 18 action-discriminated composites (`canvas_import`, `canvas_board`, `canvas_node`, `canvas_render`, `canvas_edge`, `canvas_group`, `canvas_history`, `canvas_view`, `canvas_query`, `canvas_webview`, `canvas_app`, `canvas_snapshot`, `canvas_ax_state`, `canvas_ax_work`, `canvas_ax_gate`, `canvas_ax_timeline`, `canvas_ax_delivery`, `canvas_intent`) plus 6 standalone tools (`canvas_batch`, `canvas_pin_nodes`, `canvas_invoke_command`, `canvas_ax_interaction`, `canvas_ingest_activity`, `canvas_screenshot`). `canvas_import` is the agent half of the human-consented attachment workflow: agents list/read requested imports, submit reviewable Markdown, or report unavailable; they cannot request, cancel, or commit. The 57 legacy single-purpose tools the composites replaced were removed in v0.3.0; v0.4.0 shipped the `canvas_snapshot` composite and removed the 6 deprecated snapshot standalones. Prefer composites for new MCP calls; see `docs/mcp.md` for the authoritative tool list and the legacy-to-composite migration reference.
9
+ 25 tools: 18 action-discriminated composites (`canvas_import`, `canvas_board`, `canvas_node`, `canvas_render`, `canvas_edge`, `canvas_group`, `canvas_history`, `canvas_view`, `canvas_query`, `canvas_webview`, `canvas_app`, `canvas_snapshot`, `canvas_ax_state`, `canvas_ax_work`, `canvas_ax_gate`, `canvas_ax_timeline`, `canvas_ax_delivery`, `canvas_intent`) plus 7 standalone tools (`canvas_help`, `canvas_batch`, `canvas_pin_nodes`, `canvas_invoke_command`, `canvas_ax_interaction`, `canvas_ingest_activity`, `canvas_screenshot`). `canvas_help { op }` returns one MCP registry operation's public arguments and guidance without accessing the canvas; omit `op` to list names. `canvas_import` is the agent half of the human-consented attachment workflow: agents list/read requested imports, submit reviewable Markdown, or report unavailable; they cannot request, cancel, or commit. The 57 legacy single-purpose tools the composites replaced were removed in v0.3.0; v0.4.0 shipped the `canvas_snapshot` composite and removed the 6 deprecated snapshot standalones. Prefer composites for new MCP calls; see `docs/mcp.md` for the authoritative tool list and the legacy-to-composite migration reference.
10
10
 
11
11
  ### Agent-assisted document imports
12
12
 
@@ -18,7 +18,7 @@ The `diagram` action of `canvas_app` is a thin preset in `src/server/diagram-pre
18
18
 
19
19
  15 static canvas resources: `canvas://boards`, `canvas://pinned-context`, `canvas://schema`, `canvas://layout`, `canvas://summary`, `canvas://spatial-context`, `canvas://history`, `canvas://code-graph`, `canvas://ax`, `canvas://ax-context`, `canvas://ax-timeline`, `canvas://ax-work`, `canvas://ax-pending-steering`, `canvas://ax-delivery`, `canvas://skills`; two canvas resource templates: `canvas://context{?budget,consumer,since}` and `canvas://graph{?focus,depth}`. Bundled skills additionally register per-skill `canvas://skills/<name>` resources and the `skill://{name}/{+path}` file template.
20
20
 
21
- Resource change notifications: the MCP server emits `notifications/resources/updated` when canvas state changes. Pin changes notify `canvas://pinned-context`; all mutations notify `canvas://layout`, `canvas://summary`, `canvas://spatial-context`, `canvas://history`, `canvas://code-graph`, and `canvas://graph`. This enables real-time human→agent collaboration — humans pin nodes in the browser, agents are notified immediately.
21
+ Resource change notifications: subscribe to each live URI to receive `notifications/resources/updated` when canvas state or knowledge policies change. Notifications invalidate cached data without reading it. Subscriptions belong to the connection; subscribe again after reconnecting. See [Resource subscriptions](mcp.md#resource-subscriptions-010).
22
22
 
23
23
  ### AX Primitives (host-agnostic)
24
24
 
package/docs/cli.md CHANGED
@@ -100,7 +100,7 @@ Tours persist with the board and snapshots, participate in undo, and clear with
100
100
  the board. A file containing `null` resets the tour: when none is saved, groups
101
101
  are sorted by y, then x, then ID. A saved empty `stops` array deliberately has no
102
102
  stops. SDK: `canvas.setTour(tourOrNull)` / `canvas.getTour()`, which returns
103
- the tour directly; read stops from `.stops`.
103
+ `{ tour, derived, position }` as of 0.10; read stops from `.tour.stops`.
104
104
 
105
105
  ### Driving a tour (agents)
106
106
 
package/docs/http-api.md CHANGED
@@ -3,6 +3,37 @@
3
3
  REST endpoints for all canvas operations + an SSE event stream. Works from
4
4
  any language. Default base URL: `http://localhost:4313`.
5
5
 
6
+ ## Operation cancellation and progress (0.10)
7
+
8
+ Registry-backed operation routes use the request's abort signal. Disconnecting
9
+ stops supported AX waits, cancels webpage fetching and prevents later batch
10
+ operations from starting. Completed writes remain. Cancellation is cooperative:
11
+ external app builds/runtime calls and local WebView captures cannot be interrupted
12
+ mid-call. A standalone cancelled operation returns HTTP 499 with
13
+ `{ "ok": false, "cancelled": true, "error": "Operation cancelled." }` when the
14
+ connection still permits a response. An interrupted batch retains completed
15
+ `results` and `refs` in its partial-failure body and adds `cancelled: true`.
16
+ Disconnected clients must inspect state before retrying; they may never receive
17
+ that body. Cancellation is not rollback.
18
+
19
+ Send `Accept: application/x-ndjson` to opt into batch and supported AX wait progress
20
+ over the operation response stream. Each newline-delimited JSON frame is one of:
21
+
22
+ ```json
23
+ {"type":"progress","progress":{"progress":1,"total":3,"message":"node.add"}}
24
+ {"type":"result","status":200,"body":{"ok":true,"results":[],"refs":{}}}
25
+ ```
26
+
27
+ The examples show frame shapes, not one complete batch. The terminal `result`
28
+ frame carries the ordinary operation status and body; the stream's HTTP 200 does
29
+ not establish operation success. Without this header, routes keep their ordinary
30
+ JSON status/body. Early validation errors may still be ordinary JSON. The
31
+ in-process and HTTP operation invokers accept optional `{ signal, onProgress }`;
32
+ the HTTP invoker negotiates this stream only when given a progress callback.
33
+
34
+ AX wait routes accept `timeoutMs` as well as the direct-HTTP `waitMs` parameter;
35
+ `timeoutMs` takes precedence when both are supplied.
36
+
6
37
  ## Knowledge lifecycle data (plan 017 slice 1)
7
38
 
8
39
  The slice-1 data contract now also drives the workbench, due engine and qualified agent context.
package/docs/mcp.md CHANGED
@@ -1,10 +1,92 @@
1
1
  # MCP reference
2
2
 
3
- PMX Canvas ships an MCP stdio server with **24 tools**, **15 static canvas
3
+ PMX Canvas ships an MCP stdio server with **25 tools**, **15 static canvas
4
4
  resources**, and two canvas resource templates, plus per-skill resources at
5
5
  `canvas://skills/<name>` and the `skill://{name}/{+path}` template. The server emits
6
- `notifications/resources/updated` when canvas state changes — humans pin
7
- nodes in the browser, agents are notified immediately.
6
+ `notifications/resources/updated` to subscribed connections when canvas state changes.
7
+ This uses MCP SDK v1, not SDK v2.
8
+
9
+ ## Operation help (0.10)
10
+
11
+ Call `canvas_help {}` for the sorted registry names exposed through MCP, then
12
+ `canvas_help { op: "node.add" }` for one operation's public `tool`, `description`
13
+ and JSON `inputSchema`. The schema includes required fields, public field names
14
+ and constant routing selectors (`action`, and `kind` for gates). For example,
15
+ `ax.approval.request` maps to `canvas_ax_gate` with `kind: "approval"`,
16
+ `action: "request"` and the optional `approvalAction` field.
17
+
18
+ Text contains the JSON result; structured content wraps it in `{ result }`.
19
+ Unknown names, internal/human-only operations and non-registry tools such as
20
+ `canvas_screenshot` return text-only `isError`; use `tools/list` for those tools.
21
+ Help reads registry metadata only: it never starts a canvas, writes state, records
22
+ a content read or advances a cursor. It is read-only and idempotent.
23
+
24
+ Help derives its schema and guidance from the operation registry, not from live
25
+ board state. Handlers still enforce conditional requirements and authorization.
26
+ Existing input schemas stay in `tools/list`; help does not enable lazy discovery
27
+ in hosts. Repeated equivalent output branches are grouped within each tool,
28
+ preserving operation/payload validation and partial-error receipts.
29
+
30
+ ## Tool results and effect hints (0.10)
31
+
32
+ All 25 tools advertise an object `outputSchema`. Registry-dispatched tools return
33
+ `structuredContent.result = { operation, outcome, data }`. `operation` names the
34
+ dispatched action (for example `node.add`), `outcome` is `success`, `error` or
35
+ `partial`, and `data` is the existing compact tool result. Prose results use
36
+ `data.message`. Exceptions use `operation: null`, `outcome: "error"` and
37
+ `data.message`; this does not guarantee that nothing changed before the exception.
38
+ Batch failures retain completed `results` and `refs`, with `partial` when at least
39
+ one entry completed. Check both `isError` and the outcome before using a result.
40
+
41
+ The three handwritten tools keep direct structured objects: `canvas_ax_interaction`
42
+ returns its accepted interaction, `canvas_ingest_activity` its event and reaction
43
+ records, and `canvas_screenshot` capture metadata alongside model-visible image
44
+ content. Their errors are text-only `isError` replies. Existing model-facing text
45
+ and compact/full projections remain unchanged; structured results do not introduce
46
+ additional full-card or UI payloads. Inspect the advertised schema for each tool.
47
+
48
+ All tools declare `readOnlyHint`, `destructiveHint`, `idempotentHint` and
49
+ `openWorldHint`. Composite hints cover every action, not only their read actions.
50
+ `canvas_query` is not read-only because context reads can advance a durable
51
+ consumer cursor. Hints describe effects; they grant no permission or retry guarantee.
52
+
53
+ ## Cancellation and progress (0.10)
54
+
55
+ MCP cancellation reaches the operation layer in both local and daemon-attached
56
+ connections. Supported AX waits release their listeners and timers immediately;
57
+ batches stop before the next operation and keep completed writes. Webpage fetches
58
+ receive the signal and stop before writing fetched content after cancellation.
59
+ Cancellation does not roll back a created node or other completed mutations.
60
+ If webpage creation already committed, its receipt retains `fetch.cancelled: true`.
61
+ External app builds/runtime calls and local WebView captures are not interruptible
62
+ mid-call; do not treat cancellation as proof that those side effects stopped.
63
+
64
+ Calls supplying `_meta.progressToken` receive `notifications/progress` at batch
65
+ entry boundaries and supported AX wait start/completion, with `progress`, optional
66
+ `total`, optional `message` and the same token. This works over the daemon bridge
67
+ as well as local stdio. No token means
68
+ no progress notifications. A host that aborts its connection may not receive a
69
+ partial result; inspect canvas state before retrying. See the
70
+ [HTTP streaming contract](http-api.md#operation-cancellation-and-progress-010).
71
+
72
+ ## Resource subscriptions (0.10)
73
+
74
+ The server advertises `resources.subscribe`. Send `resources/subscribe` with an
75
+ exact live URI, then `resources/unsubscribe` to stop. Subscriptions belong to the
76
+ connection and disappear on close; reconnecting clients must subscribe again.
77
+
78
+ Live resources are `canvas://layout`, `boards`, `summary`, `context`, `graph`,
79
+ `spatial-context`, `history`, `code-graph`, `pinned-context`, `ax`, `ax-context`,
80
+ `ax-timeline`, `ax-work`, `ax-pending-steering` and `ax-delivery` (each with the
81
+ `canvas://` prefix). Parameterized context and graph URIs are supported, for example
82
+ `canvas://context?consumer=my-agent&budget=3000`; notifications echo the exact
83
+ subscribed URI. Static schema and bundled skill resources do not support subscriptions.
84
+
85
+ Notifications invalidate cached data, not deliver it. Read the resource when needed.
86
+ They can be broader than the underlying change, and daemon reconnection invalidates
87
+ subscribed resources to cover missed events. Notification delivery does not record
88
+ a context read or advance a consumer cursor. Explicit `resources/read` works without
89
+ subscriptions, including on hosts that do not use this capability.
8
90
 
9
91
  > **Consolidation complete (plan-006/008).** The MCP surface shrank from 84
10
92
  > tools to 22: 16 action-discriminated **composites** (recommended — see
@@ -83,12 +165,12 @@ Add to your agent's MCP config:
83
165
  }
84
166
  ```
85
167
 
86
- The canvas auto-starts on first tool call.
168
+ The canvas auto-starts on first canvas operation, not on `canvas_help`.
87
169
 
88
170
  ## Private MCP App preview
89
171
 
90
172
  `pmx-canvas --mcp-app` runs a **separate, experimental stdio profile**; the
91
- 24-tool `--mcp` profile above is unchanged. Build from this checkout first:
173
+ 25-tool `--mcp` profile above is separate. Build from this checkout first:
92
174
 
93
175
  ```bash
94
176
  bun run build
@@ -358,7 +440,7 @@ Single-card and layout reads record delivered content, not incident relations.
358
440
  Use a complete graph or brief relation entry to acknowledge those relations.
359
441
  Reads never mark full card content read or
360
442
  switch the workbench. Pull needed cards in full separately.
361
- No new tool is added: the tool count stays 24.
443
+ These reads add no tools beyond the catalog listed above.
362
444
 
363
445
  Library search is `canvas_query { action: "search", query: "auth", scope:
364
446
  "library" }`. It returns board/card IDs and never switches the active board.
@@ -491,13 +573,14 @@ agent intends to publish its own cursor or attention target.
491
573
 
492
574
  ## Standalone tools
493
575
 
494
- 6 tools that intentionally stay outside the composites — folding them would
576
+ 7 tools that intentionally stay outside the composites — folding them would
495
577
  hurt (distinct callers, binary payloads, trust-boundary or execution-intent
496
578
  semantics). See [Migration reference](#migration-reference) below for the
497
579
  legacy tools the composites replaced.
498
580
 
499
581
  | Tool | Description |
500
582
  |------|-------------|
583
+ | `canvas_help` | List MCP registry operations or fetch one operation's public arguments and guidance (`op` optional); no canvas access |
501
584
  | `canvas_batch` | Run a batch of canvas operations with `$ref` support |
502
585
  | `canvas_pin_nodes` | Pin nodes to include in agent context (`nodeIds`, `mode` set/add/remove, optional `reason` shown to the human) |
503
586
  | `canvas_invoke_command` | Invoke a registry command (`pmx.plan`, `pmx.execute`, `pmx.promote-context`, `pmx.summarize`, `pmx.review`); records a `command` agent-event, unknown names rejected |
@@ -703,20 +786,9 @@ interaction against the node's effective capabilities.
703
786
 
704
787
  ## Change notifications
705
788
 
706
- The MCP server emits `notifications/resources/updated` whenever canvas state
707
- changes:
708
-
709
- - Pin changes notify `canvas://pinned-context`, `canvas://ax`, and `canvas://ax-context`
710
- - AX focus changes notify `canvas://ax` and `canvas://ax-context`
711
- - Canvas-bound AX mutations (work items, approval gates, review annotations,
712
- host capability) notify `canvas://ax`, `canvas://ax-work`, and `canvas://ax-context`
713
- - AX timeline mutations (agent-events, evidence, steering) notify
714
- `canvas://ax-timeline` and `canvas://ax-context`
715
- - All mutations notify `canvas://layout`, `canvas://summary`,
716
- `canvas://spatial-context`, `canvas://history`, `canvas://code-graph`, and `canvas://graph`
717
-
718
- This closes the human-to-agent loop: spatial curation in the browser becomes
719
- an immediate signal in the agent's context.
789
+ Notifications require an explicit subscription to each live resource URI. See
790
+ [Resource subscriptions](#resource-subscriptions-010) for connection lifetime,
791
+ reconnection and broad cache invalidation. Receiving a notification is not a content read.
720
792
 
721
793
  ## Codex App Adapter
722
794