@astral-mcp/projection 0.2.0 → 0.3.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.
package/dist/index.d.mts CHANGED
@@ -326,7 +326,7 @@ declare function openBlob(blob: string, options: OpenBlobOptions): BlobPayload;
326
326
  * CI instead of silently minting blobs with a stale version stamp.
327
327
  */
328
328
  /** `@astral-mcp/projection` package version (must match packages/projection/package.json). */
329
- declare const LIB_VERSION = "0.2.0";
329
+ declare const LIB_VERSION = "0.3.0";
330
330
  /** Pinned XState version (must match the exact pin in package.json — D2). */
331
331
  declare const XSTATE_VERSION = "5.32.5";
332
332
  //#endregion
@@ -435,6 +435,8 @@ interface ProjectionResource {
435
435
  description: string;
436
436
  mimeType: string;
437
437
  text: string;
438
+ /** Resource-level metadata (e.g. the MCP Apps `ui` object). */
439
+ _meta?: Record<string, unknown>;
438
440
  }
439
441
  declare const EXAMPLE_HAPPY_PATH = "import { setup, assign } from 'xstate';\nimport { toolCall } from '@astral-mcp/tools';\n\n// Read a file and return its contents. Read-only calls run immediately,\n// so this whole machine completes in one round trip.\nexport default setup({}).createMachine({\n id: 'readNotes',\n context: ({ input }) => ({ path: input?.path ?? '/notes.txt', text: null }),\n initial: 'reading',\n states: {\n reading: {\n entry: toolCall('read_file', ({ context }) => ({ path: context.path })),\n on: {\n TOOL_RESULT: {\n target: 'done',\n actions: assign({ text: ({ event }) => event.result }),\n },\n TOOL_ERROR: {\n target: 'failed',\n actions: assign({ text: ({ event }) => `error: ${event.error.message}` }),\n },\n },\n },\n done: { type: 'final' },\n failed: { type: 'final' },\n },\n output: ({ context }) => ({ text: context.text }),\n});\n";
440
442
  declare const EXAMPLE_DENIAL_FALLBACK = "import { setup, assign } from 'xstate';\nimport { toolCall } from '@astral-mcp/tools';\n\n// Write a file, but keep going if the user declines. TOOL_DENIED is an\n// ordinary transition — denial is never an automatic abort.\nexport default setup({}).createMachine({\n id: 'writeWithFallback',\n context: { status: 'pending', note: null },\n initial: 'writing',\n states: {\n writing: {\n entry: toolCall('write_file', () => ({ path: '/notes.txt', content: 'hello' })),\n on: {\n TOOL_RESULT: {\n target: 'summarizing',\n actions: assign({ status: 'written' }),\n },\n TOOL_DENIED: {\n // The user said no: record it and carry on with the read-only\n // part of the work instead of failing the whole workflow.\n target: 'summarizing',\n actions: assign({\n status: 'skipped',\n note: ({ event }) => event.reason ?? 'declined by user',\n }),\n },\n TOOL_ERROR: {\n target: 'summarizing',\n actions: assign({ status: 'failed' }),\n },\n },\n },\n summarizing: {\n entry: toolCall('list_dir', () => ({ path: '/' })),\n on: {\n TOOL_RESULT: { target: 'done' },\n TOOL_ERROR: { target: 'done' },\n },\n },\n done: { type: 'final' },\n },\n output: ({ context }) => ({ status: context.status, note: context.note }),\n});\n";
@@ -508,6 +510,40 @@ interface ProjectionFacade {
508
510
  */
509
511
  declare function wrapMcpServer(source: McpServer | Client, options: WrapOptions): Promise<ProjectionFacade>;
510
512
  //#endregion
513
+ //#region src/apps/dashboard.d.ts
514
+ /**
515
+ * MCP Apps workflow dashboard (PRD §12.1, second fenced use case).
516
+ *
517
+ * A static, library-authored HTML app that renders an `execute_machine`
518
+ * result — output, approval audit trail, console logs, or the structured
519
+ * error — inside the host's sandboxed iframe, per the MCP Apps extension
520
+ * (SEP-1865, spec 2026-01-26).
521
+ *
522
+ * Security posture (the §12.1 design constraint, satisfied by construction):
523
+ * - The HTML is a compile-time constant. No model-authored code, no
524
+ * template interpolation, no VM: UI rendering can never reach the tools
525
+ * bridge because nothing dynamic executes server-side at all.
526
+ * - Model-controlled data (outputs, audit args, log lines, error text)
527
+ * enters the DOM exclusively through `textContent` — the app never uses
528
+ * innerHTML — so hostile strings render inert (S6 extended to the app).
529
+ * - The app requests no host capabilities: `appCapabilities: {}`. It cannot
530
+ * call tools, open links, or write context; it only listens.
531
+ *
532
+ * Wire contract implemented (app side):
533
+ * - `ui/initialize` request → await result → `ui/notifications/initialized`.
534
+ * - Listens for `ui/notifications/tool-input`, `ui/notifications/tool-result`
535
+ * and `ui/notifications/tool-cancelled`; ignores everything else.
536
+ * - The result payload is the standard `CallToolResult` from
537
+ * `execute_machine`: `structuredContent` is either
538
+ * `{ output, approvals, logs }` or (with `isError`) the structured error
539
+ * `{ phase, message, hint, logs }`.
540
+ */
541
+ /** URI the dashboard is served under. MCP Apps mandates the ui:// scheme. */
542
+ declare const DASHBOARD_RESOURCE_URI = "ui://projection/dashboard.html";
543
+ /** MIME type the MCP Apps spec mandates for app HTML resources. */
544
+ declare const MCP_APP_MIME_TYPE = "text/html;profile=mcp-app";
545
+ declare const DASHBOARD_HTML = "<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\" />\n<title>Projection workflow dashboard</title>\n<style>\n :root {\n color-scheme: light dark;\n --bg: #ffffff; --fg: #1a1a2e; --muted: #6b7280; --line: #e5e7eb;\n --ok: #047857; --ok-bg: #ecfdf5; --err: #b91c1c; --err-bg: #fef2f2;\n --chip: #eef2ff; --mono: ui-monospace, SFMono-Regular, Menlo, monospace;\n }\n @media (prefers-color-scheme: dark) {\n :root {\n --bg: #111318; --fg: #e5e7eb; --muted: #9ca3af; --line: #2a2f3a;\n --ok: #34d399; --ok-bg: #06281f; --err: #f87171; --err-bg: #2d1215;\n --chip: #1e2433;\n }\n }\n html, body { margin: 0; padding: 0; background: var(--bg); color: var(--fg);\n font: 14px/1.5 system-ui, sans-serif; }\n main { padding: 16px; max-width: 720px; }\n h1 { font-size: 15px; margin: 0 0 12px; display: flex; gap: 8px; align-items: center; }\n .chip { font-size: 12px; padding: 2px 10px; border-radius: 999px; background: var(--chip); }\n .chip.ok { color: var(--ok); background: var(--ok-bg); }\n .chip.err { color: var(--err); background: var(--err-bg); }\n section { margin: 14px 0; }\n section > h2 { font-size: 12px; text-transform: uppercase; letter-spacing: .06em;\n color: var(--muted); margin: 0 0 6px; }\n pre { margin: 0; padding: 10px; background: var(--chip); border-radius: 8px;\n font-family: var(--mono); font-size: 12px; overflow-x: auto; white-space: pre-wrap; }\n table { border-collapse: collapse; width: 100%; font-size: 13px; }\n th, td { text-align: left; padding: 6px 8px; border-bottom: 1px solid var(--line);\n vertical-align: top; }\n th { color: var(--muted); font-weight: 500; }\n td.args { font-family: var(--mono); font-size: 12px; }\n .log { font-family: var(--mono); font-size: 12px; padding: 2px 0; }\n .log .level { color: var(--muted); margin-right: 6px; }\n .hint { color: var(--muted); font-size: 13px; margin-top: 6px; }\n .hidden { display: none; }\n #waiting { color: var(--muted); }\n</style>\n</head>\n<body>\n<main>\n <h1>Projection workflow <span id=\"status\" class=\"chip\">waiting…</span></h1>\n <p id=\"waiting\">Waiting for the workflow result from the host…</p>\n <section id=\"output-section\" class=\"hidden\"><h2>Output</h2><pre id=\"output\"></pre></section>\n <section id=\"error-section\" class=\"hidden\"><h2>Error</h2>\n <pre id=\"error\"></pre><p id=\"error-hint\" class=\"hint\"></p></section>\n <section id=\"approvals-section\" class=\"hidden\"><h2>Approval audit trail</h2>\n <table><thead><tr><th>Tool</th><th>Decision</th><th>Arguments</th></tr></thead>\n <tbody id=\"approvals\"></tbody></table></section>\n <section id=\"logs-section\" class=\"hidden\"><h2>Console</h2><div id=\"logs\"></div></section>\n</main>\n<script>\n'use strict';\n(function () {\n var nextId = 1;\n var pending = new Map();\n\n function post(message) { window.parent.postMessage(message, '*'); }\n function request(method, params) {\n return new Promise(function (resolve, reject) {\n var id = nextId++;\n pending.set(id, { resolve: resolve, reject: reject });\n post({ jsonrpc: '2.0', id: id, method: method, params: params });\n });\n }\n function notify(method, params) {\n var message = { jsonrpc: '2.0', method: method };\n if (params !== undefined) message.params = params;\n post(message);\n }\n\n function byId(id) { return document.getElementById(id); }\n function show(id) { byId(id).classList.remove('hidden'); }\n function setStatus(text, cls) {\n var chip = byId('status');\n chip.textContent = text;\n chip.className = 'chip' + (cls ? ' ' + cls : '');\n byId('waiting').classList.add('hidden');\n }\n function pretty(value) {\n try { return JSON.stringify(value, null, 2); } catch (e) { return String(value); }\n }\n\n // All model-controlled data enters the DOM via textContent only.\n function renderResult(result) {\n var structured = (result && result.structuredContent) || {};\n if (result && result.isError) {\n setStatus('failed: ' + String(structured.phase || 'error'), 'err');\n byId('error').textContent =\n String(structured.message || 'unknown error');\n byId('error-hint').textContent = String(structured.hint || '');\n show('error-section');\n } else {\n setStatus('completed', 'ok');\n byId('output').textContent = pretty(\n structured.output === undefined ? null : structured.output,\n );\n show('output-section');\n }\n var approvals = Array.isArray(structured.approvals) ? structured.approvals : [];\n if (approvals.length > 0) {\n var tbody = byId('approvals');\n approvals.forEach(function (entry) {\n var row = document.createElement('tr');\n var tool = document.createElement('td');\n tool.textContent = String(entry.tool || '');\n var decision = document.createElement('td');\n decision.textContent = String(entry.decision || '');\n var args = document.createElement('td');\n args.className = 'args';\n args.textContent = pretty(entry.args || {});\n row.appendChild(tool); row.appendChild(decision); row.appendChild(args);\n tbody.appendChild(row);\n });\n show('approvals-section');\n }\n var logs = Array.isArray(structured.logs) ? structured.logs : [];\n if (logs.length > 0) {\n var list = byId('logs');\n logs.forEach(function (entry) {\n var line = document.createElement('div');\n line.className = 'log';\n var level = document.createElement('span');\n level.className = 'level';\n level.textContent = '[' + String(entry.level || 'log') + ']';\n line.appendChild(level);\n line.appendChild(document.createTextNode(String(entry.message || '')));\n list.appendChild(line);\n });\n show('logs-section');\n }\n }\n\n window.addEventListener('message', function (event) {\n // Only the embedding host may speak: forged tool-results from a hostile\n // embedder could otherwise spoof the displayed audit trail.\n if (event.source !== window.parent) return;\n var message = event.data;\n if (!message || message.jsonrpc !== '2.0') return;\n if (message.id !== undefined && message.method === undefined) {\n var waiter = pending.get(message.id);\n if (waiter) {\n pending.delete(message.id);\n if (message.error) waiter.reject(message.error);\n else waiter.resolve(message.result);\n }\n return;\n }\n if (message.method === 'ui/notifications/tool-input') {\n setStatus('running…');\n return;\n }\n if (message.method === 'ui/notifications/tool-result') {\n renderResult(message.params);\n return;\n }\n if (message.method === 'ui/notifications/tool-cancelled') {\n var reason = message.params && message.params.reason;\n setStatus('cancelled', 'err');\n byId('error').textContent = String(reason || 'cancelled by the host');\n show('error-section');\n return;\n }\n // Host -> app REQUESTS (id + method) get an empty result: answers ping\n // and ui/resource-teardown so hosts don't stall on their SHOULD-wait.\n if (message.id !== undefined && message.method !== undefined) {\n post({ jsonrpc: '2.0', id: message.id, result: {} });\n }\n });\n\n // Handshake: initialize, then signal readiness. The host MUST NOT push\n // tool data before ui/notifications/initialized.\n request('ui/initialize', { appCapabilities: {} }).then(function () {\n notify('ui/notifications/initialized');\n });\n})();\n</script>\n</body>\n</html>\n";
546
+ //#endregion
511
547
  //#region src/errors.d.ts
512
548
  /**
513
549
  * Error carrying the structured `isError` body every failure returns (R25).
@@ -858,5 +894,5 @@ declare function runSegment(options: RunSegmentOptions): Promise<SegmentOutcome>
858
894
  */
859
895
  declare const AUTHORING_CONTRACT = "Projection authoring contract (execute_machine)\n\nWrite ONE module whose only top-level statements are imports and a single\ndefault-exported XState v5 machine. The module is re-evaluated on every\napproval round trip, so top-level side effects are rejected.\n\n import { setup } from 'xstate';\n import { toolCall } from '@astral-mcp/tools';\n\n export default setup({}).createMachine({ /* ... */ });\n\nRules:\n1. Default-export a machine built with XState v5 setup(...).createMachine(...).\n XState v4 Machine(...) is rejected.\n2. Machine context must be JSON-serializable at all times. No Map, Set, Date,\n class instances, functions, undefined, bigint or symbols in context.\n3. Request tools with the toolCall action. NEVER wrap a tool call in\n fromPromise/invoke — restored snapshots restart invoked actors, which\n would re-execute an approved write. Machines using invoke are rejected.\n4. After a toolCall, wait in a state for one of these events:\n TOOL_RESULT { callId, result } the call succeeded\n TOOL_ERROR { callId, error } the call failed\n TOOL_DENIED { callId, reason } the user declined, or policy denies it\n Denial is not an abort: handle TOOL_DENIED and continue or finish.\n IMPORTANT: if your machine has more than one call outstanding at the same\n time (parallel regions), give each toolCall an explicit id and guard every\n waiting state with whenCall(id) — events reach every region that can\n handle them, so an unguarded region will consume another call's result:\n\n import { toolCall, whenCall } from '@astral-mcp/tools';\n entry: toolCall('write_file', () => ({ path: '/a' }), { id: 'a' }),\n on: { TOOL_RESULT: { guard: whenCall('a'), target: 'done' } },\n5. Read-only tools run immediately. Every other tool pauses the machine for\n user approval; the arguments captured at that moment are the arguments\n that execute.\n6. Only 'xstate' and '@astral-mcp/tools' resolve as imports. There is no\n filesystem, no network, no fetch. All working state lives in context.\n7. Finish by reaching a final state. Use the machine's output for the result.\n\nExample — happy path with a read-only call:\n\n import { setup, assign } from 'xstate';\n import { toolCall } from '@astral-mcp/tools';\n\n export default setup({}).createMachine({\n id: 'readNotes',\n context: ({ input }) => ({ path: input?.path ?? '/notes.txt', text: null }),\n initial: 'reading',\n states: {\n reading: {\n entry: toolCall('read_file', ({ context }) => ({ path: context.path })),\n on: {\n TOOL_RESULT: { target: 'done', actions: assign({ text: ({ event }) => event.result }) },\n TOOL_ERROR: 'failed',\n },\n },\n done: { type: 'final' },\n failed: { type: 'final' },\n },\n output: ({ context }) => ({ text: context.text }),\n });\n\nExample — handling a denied approval:\n\n import { setup, assign } from 'xstate';\n import { toolCall } from '@astral-mcp/tools';\n\n export default setup({}).createMachine({\n id: 'writeNotes',\n context: { status: 'pending' },\n initial: 'writing',\n states: {\n writing: {\n entry: toolCall('write_file', () => ({ path: '/notes.txt', content: 'hello' })),\n on: {\n TOOL_RESULT: { target: 'done', actions: assign({ status: 'written' }) },\n TOOL_DENIED: { target: 'done', actions: assign({ status: 'skipped' }) },\n TOOL_ERROR: { target: 'done', actions: assign({ status: 'failed' }) },\n },\n },\n done: { type: 'final' },\n },\n output: ({ context }) => ({ status: context.status }),\n });\n";
860
896
  //#endregion
861
- export { APPROVAL_SCHEMA, AUTHORING_CONTRACT, ApprovalVerdict, AuditEntry, BLOB_SCHEMA_VERSION, BlobPayload, BlobSealerConfig, type CallToolResult, type ContentBlock, DEFAULT_CAPS, DEFAULT_CODE_MODE_PREAMBLE, DEFAULT_MAX_BLOB_BYTES, DEFAULT_SERVER_ALIAS, DEFAULT_TTL_MS, Diagnostic, ENVELOPE_PREFIX, EXAMPLE_DENIAL_FALLBACK, EXAMPLE_HAPPY_PATH, type ElicitationCopy, type ElicitationCopyOptions, ErrorPhase, type ExecuteMachineArgs, type ExecuteMachineDeps, type GeneratedToolTypes, HostEvent, type InputRequest, type InputRequests, type InputRequiredResult, type InputResponse, type InputResponseView, type InputResponses, Introspector, LIB_VERSION, LogEntry, LogLevel, MAX_CAPS, MRTR_PROTOCOL_REVISION, type OpenBlobOptions, POLICY_DENIED_REASON, PendingToolCall, PolicyDecision, PolicyOverrides, ProjectionConfig, ProjectionError, type ProjectionFacade, type ProjectionResource, type ProtocolEra, RESOURCE_URIS, RedactionHook, type RegisterOptions, type ResolvedCallPolicy, ResourceCaps, type RetryEnvelope, type RunSegmentOptions, type SandboxSession, type SandboxSessionOptions, type SandboxToolCall, type SandboxToolCallHandler, SegmentOutcome, ServerAlias, ServerCatalog, StructuredError, TOOLS_MODULE_SPECIFIER, TOOL_CALL_HOST_GLOBAL, ToolAnnotations, ToolCallOutcome, type ToolCallResult, ToolCatalog, ToolCatalogEntry, ToolExecutor, type WrapOptions, XSTATE_VERSION, acceptedContent, buildBlobPayload, buildElicitationCopy, buildInstructions, buildResources, capLength, clampCaps, createIntrospector, createSandboxSession, decidePolicy, discoverApis, executeMachine, findTool, generateToolTypes, hashCode, inputRequired, inputResponse, isInputRequiredResult, isRecord, neutralizeControlChars, openBlob, policyDeniedReason, resolveCallPolicy, runSegment, schemaRequiresArgs, schemaToTsType, sealBlob, supportsMrtr, toStructuredError, wrapMcpServer };
897
+ export { APPROVAL_SCHEMA, AUTHORING_CONTRACT, ApprovalVerdict, AuditEntry, BLOB_SCHEMA_VERSION, BlobPayload, BlobSealerConfig, type CallToolResult, type ContentBlock, DASHBOARD_HTML, DASHBOARD_RESOURCE_URI, DEFAULT_CAPS, DEFAULT_CODE_MODE_PREAMBLE, DEFAULT_MAX_BLOB_BYTES, DEFAULT_SERVER_ALIAS, DEFAULT_TTL_MS, Diagnostic, ENVELOPE_PREFIX, EXAMPLE_DENIAL_FALLBACK, EXAMPLE_HAPPY_PATH, type ElicitationCopy, type ElicitationCopyOptions, ErrorPhase, type ExecuteMachineArgs, type ExecuteMachineDeps, type GeneratedToolTypes, HostEvent, type InputRequest, type InputRequests, type InputRequiredResult, type InputResponse, type InputResponseView, type InputResponses, Introspector, LIB_VERSION, LogEntry, LogLevel, MAX_CAPS, MCP_APP_MIME_TYPE, MRTR_PROTOCOL_REVISION, type OpenBlobOptions, POLICY_DENIED_REASON, PendingToolCall, PolicyDecision, PolicyOverrides, ProjectionConfig, ProjectionError, type ProjectionFacade, type ProjectionResource, type ProtocolEra, RESOURCE_URIS, RedactionHook, type RegisterOptions, type ResolvedCallPolicy, ResourceCaps, type RetryEnvelope, type RunSegmentOptions, type SandboxSession, type SandboxSessionOptions, type SandboxToolCall, type SandboxToolCallHandler, SegmentOutcome, ServerAlias, ServerCatalog, StructuredError, TOOLS_MODULE_SPECIFIER, TOOL_CALL_HOST_GLOBAL, ToolAnnotations, ToolCallOutcome, type ToolCallResult, ToolCatalog, ToolCatalogEntry, ToolExecutor, type WrapOptions, XSTATE_VERSION, acceptedContent, buildBlobPayload, buildElicitationCopy, buildInstructions, buildResources, capLength, clampCaps, createIntrospector, createSandboxSession, decidePolicy, discoverApis, executeMachine, findTool, generateToolTypes, hashCode, inputRequired, inputResponse, isInputRequiredResult, isRecord, neutralizeControlChars, openBlob, policyDeniedReason, resolveCallPolicy, runSegment, schemaRequiresArgs, schemaToTsType, sealBlob, supportsMrtr, toStructuredError, wrapMcpServer };
862
898
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/blob/sealer.ts","../src/blob/versions.ts","../src/mrtr/protocol.ts","../src/mrtr/adapter.ts","../src/mrtr/discover.ts","../src/wrap.ts","../src/errors.ts","../src/introspector/introspector.ts","../src/typegen/generate.ts","../src/typegen/json-schema.ts","../src/text-safety.ts","../src/policy/decision.ts","../src/policy/elicitation.ts","../src/sandbox/session.ts","../src/machine-host/host.ts","../src/machine-host/contract.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/blob/sealer.ts","../src/blob/versions.ts","../src/mrtr/protocol.ts","../src/mrtr/adapter.ts","../src/mrtr/discover.ts","../src/wrap.ts","../src/apps/dashboard.ts","../src/errors.ts","../src/introspector/introspector.ts","../src/typegen/generate.ts","../src/typegen/json-schema.ts","../src/text-safety.ts","../src/policy/decision.ts","../src/policy/elicitation.ts","../src/sandbox/session.ts","../src/machine-host/host.ts","../src/machine-host/contract.ts"],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -7,6 +7,223 @@ import { deflateRawSync, inflateRawSync } from "node:zlib";
7
7
  import { getModuleLoader, getTypescriptSupport, loadQuickJs } from "@sebastianwessel/quickjs";
8
8
  import { readFileSync, readdirSync } from "node:fs";
9
9
  import { dirname, join, resolve } from "node:path";
10
+ //#region src/apps/dashboard.ts
11
+ /**
12
+ * MCP Apps workflow dashboard (PRD §12.1, second fenced use case).
13
+ *
14
+ * A static, library-authored HTML app that renders an `execute_machine`
15
+ * result — output, approval audit trail, console logs, or the structured
16
+ * error — inside the host's sandboxed iframe, per the MCP Apps extension
17
+ * (SEP-1865, spec 2026-01-26).
18
+ *
19
+ * Security posture (the §12.1 design constraint, satisfied by construction):
20
+ * - The HTML is a compile-time constant. No model-authored code, no
21
+ * template interpolation, no VM: UI rendering can never reach the tools
22
+ * bridge because nothing dynamic executes server-side at all.
23
+ * - Model-controlled data (outputs, audit args, log lines, error text)
24
+ * enters the DOM exclusively through `textContent` — the app never uses
25
+ * innerHTML — so hostile strings render inert (S6 extended to the app).
26
+ * - The app requests no host capabilities: `appCapabilities: {}`. It cannot
27
+ * call tools, open links, or write context; it only listens.
28
+ *
29
+ * Wire contract implemented (app side):
30
+ * - `ui/initialize` request → await result → `ui/notifications/initialized`.
31
+ * - Listens for `ui/notifications/tool-input`, `ui/notifications/tool-result`
32
+ * and `ui/notifications/tool-cancelled`; ignores everything else.
33
+ * - The result payload is the standard `CallToolResult` from
34
+ * `execute_machine`: `structuredContent` is either
35
+ * `{ output, approvals, logs }` or (with `isError`) the structured error
36
+ * `{ phase, message, hint, logs }`.
37
+ */
38
+ /** URI the dashboard is served under. MCP Apps mandates the ui:// scheme. */
39
+ const DASHBOARD_RESOURCE_URI = "ui://projection/dashboard.html";
40
+ /** MIME type the MCP Apps spec mandates for app HTML resources. */
41
+ const MCP_APP_MIME_TYPE = "text/html;profile=mcp-app";
42
+ const DASHBOARD_HTML = `<!doctype html>
43
+ <html lang="en">
44
+ <head>
45
+ <meta charset="utf-8" />
46
+ <title>Projection workflow dashboard</title>
47
+ <style>
48
+ :root {
49
+ color-scheme: light dark;
50
+ --bg: #ffffff; --fg: #1a1a2e; --muted: #6b7280; --line: #e5e7eb;
51
+ --ok: #047857; --ok-bg: #ecfdf5; --err: #b91c1c; --err-bg: #fef2f2;
52
+ --chip: #eef2ff; --mono: ui-monospace, SFMono-Regular, Menlo, monospace;
53
+ }
54
+ @media (prefers-color-scheme: dark) {
55
+ :root {
56
+ --bg: #111318; --fg: #e5e7eb; --muted: #9ca3af; --line: #2a2f3a;
57
+ --ok: #34d399; --ok-bg: #06281f; --err: #f87171; --err-bg: #2d1215;
58
+ --chip: #1e2433;
59
+ }
60
+ }
61
+ html, body { margin: 0; padding: 0; background: var(--bg); color: var(--fg);
62
+ font: 14px/1.5 system-ui, sans-serif; }
63
+ main { padding: 16px; max-width: 720px; }
64
+ h1 { font-size: 15px; margin: 0 0 12px; display: flex; gap: 8px; align-items: center; }
65
+ .chip { font-size: 12px; padding: 2px 10px; border-radius: 999px; background: var(--chip); }
66
+ .chip.ok { color: var(--ok); background: var(--ok-bg); }
67
+ .chip.err { color: var(--err); background: var(--err-bg); }
68
+ section { margin: 14px 0; }
69
+ section > h2 { font-size: 12px; text-transform: uppercase; letter-spacing: .06em;
70
+ color: var(--muted); margin: 0 0 6px; }
71
+ pre { margin: 0; padding: 10px; background: var(--chip); border-radius: 8px;
72
+ font-family: var(--mono); font-size: 12px; overflow-x: auto; white-space: pre-wrap; }
73
+ table { border-collapse: collapse; width: 100%; font-size: 13px; }
74
+ th, td { text-align: left; padding: 6px 8px; border-bottom: 1px solid var(--line);
75
+ vertical-align: top; }
76
+ th { color: var(--muted); font-weight: 500; }
77
+ td.args { font-family: var(--mono); font-size: 12px; }
78
+ .log { font-family: var(--mono); font-size: 12px; padding: 2px 0; }
79
+ .log .level { color: var(--muted); margin-right: 6px; }
80
+ .hint { color: var(--muted); font-size: 13px; margin-top: 6px; }
81
+ .hidden { display: none; }
82
+ #waiting { color: var(--muted); }
83
+ </style>
84
+ </head>
85
+ <body>
86
+ <main>
87
+ <h1>Projection workflow <span id="status" class="chip">waiting…</span></h1>
88
+ <p id="waiting">Waiting for the workflow result from the host…</p>
89
+ <section id="output-section" class="hidden"><h2>Output</h2><pre id="output"></pre></section>
90
+ <section id="error-section" class="hidden"><h2>Error</h2>
91
+ <pre id="error"></pre><p id="error-hint" class="hint"></p></section>
92
+ <section id="approvals-section" class="hidden"><h2>Approval audit trail</h2>
93
+ <table><thead><tr><th>Tool</th><th>Decision</th><th>Arguments</th></tr></thead>
94
+ <tbody id="approvals"></tbody></table></section>
95
+ <section id="logs-section" class="hidden"><h2>Console</h2><div id="logs"></div></section>
96
+ </main>
97
+ <script>
98
+ 'use strict';
99
+ (function () {
100
+ var nextId = 1;
101
+ var pending = new Map();
102
+
103
+ function post(message) { window.parent.postMessage(message, '*'); }
104
+ function request(method, params) {
105
+ return new Promise(function (resolve, reject) {
106
+ var id = nextId++;
107
+ pending.set(id, { resolve: resolve, reject: reject });
108
+ post({ jsonrpc: '2.0', id: id, method: method, params: params });
109
+ });
110
+ }
111
+ function notify(method, params) {
112
+ var message = { jsonrpc: '2.0', method: method };
113
+ if (params !== undefined) message.params = params;
114
+ post(message);
115
+ }
116
+
117
+ function byId(id) { return document.getElementById(id); }
118
+ function show(id) { byId(id).classList.remove('hidden'); }
119
+ function setStatus(text, cls) {
120
+ var chip = byId('status');
121
+ chip.textContent = text;
122
+ chip.className = 'chip' + (cls ? ' ' + cls : '');
123
+ byId('waiting').classList.add('hidden');
124
+ }
125
+ function pretty(value) {
126
+ try { return JSON.stringify(value, null, 2); } catch (e) { return String(value); }
127
+ }
128
+
129
+ // All model-controlled data enters the DOM via textContent only.
130
+ function renderResult(result) {
131
+ var structured = (result && result.structuredContent) || {};
132
+ if (result && result.isError) {
133
+ setStatus('failed: ' + String(structured.phase || 'error'), 'err');
134
+ byId('error').textContent =
135
+ String(structured.message || 'unknown error');
136
+ byId('error-hint').textContent = String(structured.hint || '');
137
+ show('error-section');
138
+ } else {
139
+ setStatus('completed', 'ok');
140
+ byId('output').textContent = pretty(
141
+ structured.output === undefined ? null : structured.output,
142
+ );
143
+ show('output-section');
144
+ }
145
+ var approvals = Array.isArray(structured.approvals) ? structured.approvals : [];
146
+ if (approvals.length > 0) {
147
+ var tbody = byId('approvals');
148
+ approvals.forEach(function (entry) {
149
+ var row = document.createElement('tr');
150
+ var tool = document.createElement('td');
151
+ tool.textContent = String(entry.tool || '');
152
+ var decision = document.createElement('td');
153
+ decision.textContent = String(entry.decision || '');
154
+ var args = document.createElement('td');
155
+ args.className = 'args';
156
+ args.textContent = pretty(entry.args || {});
157
+ row.appendChild(tool); row.appendChild(decision); row.appendChild(args);
158
+ tbody.appendChild(row);
159
+ });
160
+ show('approvals-section');
161
+ }
162
+ var logs = Array.isArray(structured.logs) ? structured.logs : [];
163
+ if (logs.length > 0) {
164
+ var list = byId('logs');
165
+ logs.forEach(function (entry) {
166
+ var line = document.createElement('div');
167
+ line.className = 'log';
168
+ var level = document.createElement('span');
169
+ level.className = 'level';
170
+ level.textContent = '[' + String(entry.level || 'log') + ']';
171
+ line.appendChild(level);
172
+ line.appendChild(document.createTextNode(String(entry.message || '')));
173
+ list.appendChild(line);
174
+ });
175
+ show('logs-section');
176
+ }
177
+ }
178
+
179
+ window.addEventListener('message', function (event) {
180
+ // Only the embedding host may speak: forged tool-results from a hostile
181
+ // embedder could otherwise spoof the displayed audit trail.
182
+ if (event.source !== window.parent) return;
183
+ var message = event.data;
184
+ if (!message || message.jsonrpc !== '2.0') return;
185
+ if (message.id !== undefined && message.method === undefined) {
186
+ var waiter = pending.get(message.id);
187
+ if (waiter) {
188
+ pending.delete(message.id);
189
+ if (message.error) waiter.reject(message.error);
190
+ else waiter.resolve(message.result);
191
+ }
192
+ return;
193
+ }
194
+ if (message.method === 'ui/notifications/tool-input') {
195
+ setStatus('running…');
196
+ return;
197
+ }
198
+ if (message.method === 'ui/notifications/tool-result') {
199
+ renderResult(message.params);
200
+ return;
201
+ }
202
+ if (message.method === 'ui/notifications/tool-cancelled') {
203
+ var reason = message.params && message.params.reason;
204
+ setStatus('cancelled', 'err');
205
+ byId('error').textContent = String(reason || 'cancelled by the host');
206
+ show('error-section');
207
+ return;
208
+ }
209
+ // Host -> app REQUESTS (id + method) get an empty result: answers ping
210
+ // and ui/resource-teardown so hosts don't stall on their SHOULD-wait.
211
+ if (message.id !== undefined && message.method !== undefined) {
212
+ post({ jsonrpc: '2.0', id: message.id, result: {} });
213
+ }
214
+ });
215
+
216
+ // Handshake: initialize, then signal readiness. The host MUST NOT push
217
+ // tool data before ui/notifications/initialized.
218
+ request('ui/initialize', { appCapabilities: {} }).then(function () {
219
+ notify('ui/notifications/initialized');
220
+ });
221
+ })();
222
+ <\/script>
223
+ </body>
224
+ </html>
225
+ `;
226
+ //#endregion
10
227
  //#region src/errors.ts
11
228
  /**
12
229
  * Error carrying the structured `isError` body every failure returns (R25).
@@ -378,7 +595,7 @@ const BLOB_SCHEMA_VERSION = 1;
378
595
  * CI instead of silently minting blobs with a stale version stamp.
379
596
  */
380
597
  /** `@astral-mcp/projection` package version (must match packages/projection/package.json). */
381
- const LIB_VERSION = "0.2.0";
598
+ const LIB_VERSION = "0.3.0";
382
599
  /** Pinned XState version (must match the exact pin in package.json — D2). */
383
600
  const XSTATE_VERSION = "5.32.5";
384
601
  //#endregion
@@ -604,7 +821,7 @@ function openBlob(blob, options) {
604
821
  }
605
822
  if (!isRecord(payload)) throw verificationError("blob payload malformed: not a JSON object", HINT_CORRUPT);
606
823
  if (payload["v"] !== 1) throw verificationError(`unknown blob schema version ${JSON.stringify(payload["v"])} (this library expects 1)`, HINT_VERSION);
607
- if (payload["libv"] !== "0.2.0") throw verificationError(`library version mismatch: blob was sealed by @astral-mcp/projection ${JSON.stringify(payload["libv"])}, this instance runs ${LIB_VERSION}`, HINT_VERSION);
824
+ if (payload["libv"] !== "0.3.0") throw verificationError(`library version mismatch: blob was sealed by @astral-mcp/projection ${JSON.stringify(payload["libv"])}, this instance runs ${LIB_VERSION}`, HINT_VERSION);
608
825
  if (payload["xstatev"] !== "5.32.5") throw verificationError(`xstate version mismatch: blob snapshot was produced under xstate ${JSON.stringify(payload["xstatev"])}, this instance runs ${XSTATE_VERSION}`, HINT_VERSION);
609
826
  assertPayloadShape(payload);
610
827
  const verified = payload;
@@ -2368,6 +2585,14 @@ function buildResources(catalog, dts) {
2368
2585
  description: "Example machine: continues gracefully when approval is declined.",
2369
2586
  mimeType: "text/plain",
2370
2587
  text: EXAMPLE_DENIAL_FALLBACK
2588
+ },
2589
+ {
2590
+ uri: DASHBOARD_RESOURCE_URI,
2591
+ name: "dashboard.html",
2592
+ description: "Workflow result dashboard rendered by MCP Apps hosts.",
2593
+ mimeType: MCP_APP_MIME_TYPE,
2594
+ text: DASHBOARD_HTML,
2595
+ _meta: { ui: { prefersBorder: true } }
2371
2596
  }
2372
2597
  ];
2373
2598
  }
@@ -2385,7 +2610,7 @@ function discoverApis(catalog, overview) {
2385
2610
  "",
2386
2611
  AUTHORING_CONTRACT
2387
2612
  ].join("\n");
2388
- const links = buildResources(catalog, "").map((resource) => ({
2613
+ const links = buildResources(catalog, "").filter((resource) => !resource.uri.startsWith("ui://")).map((resource) => ({
2389
2614
  type: "resource_link",
2390
2615
  uri: resource.uri,
2391
2616
  name: resource.name,
@@ -2875,7 +3100,8 @@ var Facade = class {
2875
3100
  server.registerTool("execute_machine", {
2876
3101
  title: "Execute a state machine",
2877
3102
  description: EXECUTE_MACHINE_DESCRIPTION,
2878
- inputSchema: EXECUTE_MACHINE_INPUT_SCHEMA
3103
+ inputSchema: EXECUTE_MACHINE_INPUT_SCHEMA,
3104
+ _meta: { ui: { resourceUri: DASHBOARD_RESOURCE_URI } }
2879
3105
  }, async (args, ctx) => await this.executeMachine({
2880
3106
  code: args.code,
2881
3107
  ...args.lang !== void 0 ? { lang: args.lang } : {},
@@ -2890,7 +3116,8 @@ var Facade = class {
2890
3116
  }, () => this.discoverApis());
2891
3117
  for (const resource of this.resources) server.registerResource(resource.name, resource.uri, {
2892
3118
  description: resource.description,
2893
- mimeType: resource.mimeType
3119
+ mimeType: resource.mimeType,
3120
+ ...resource._meta !== void 0 ? { _meta: resource._meta } : {}
2894
3121
  }, () => ({ contents: [{
2895
3122
  uri: resource.uri,
2896
3123
  mimeType: resource.mimeType,
@@ -2924,6 +3151,6 @@ const EXECUTE_MACHINE_INPUT_SCHEMA = z.object({
2924
3151
  input: z.unknown().optional().describe("Passed to the machine as its `input`.")
2925
3152
  });
2926
3153
  //#endregion
2927
- export { APPROVAL_SCHEMA, AUTHORING_CONTRACT, BLOB_SCHEMA_VERSION, DEFAULT_CAPS, DEFAULT_CODE_MODE_PREAMBLE, DEFAULT_MAX_BLOB_BYTES, DEFAULT_SERVER_ALIAS, DEFAULT_TTL_MS, ENVELOPE_PREFIX, EXAMPLE_DENIAL_FALLBACK, EXAMPLE_HAPPY_PATH, Introspector, LIB_VERSION, MAX_CAPS, MRTR_PROTOCOL_REVISION, POLICY_DENIED_REASON, ProjectionError, RESOURCE_URIS, TOOLS_MODULE_SPECIFIER, TOOL_CALL_HOST_GLOBAL, XSTATE_VERSION, acceptedContent, buildBlobPayload, buildElicitationCopy, buildInstructions, buildResources, capLength, clampCaps, createIntrospector, createSandboxSession, decidePolicy, discoverApis, executeMachine, findTool, generateToolTypes, hashCode, inputRequired, inputResponse, isInputRequiredResult, isRecord, neutralizeControlChars, openBlob, policyDeniedReason, resolveCallPolicy, runSegment, schemaRequiresArgs, schemaToTsType, sealBlob, supportsMrtr, toStructuredError, wrapMcpServer };
3154
+ export { APPROVAL_SCHEMA, AUTHORING_CONTRACT, BLOB_SCHEMA_VERSION, DASHBOARD_HTML, DASHBOARD_RESOURCE_URI, DEFAULT_CAPS, DEFAULT_CODE_MODE_PREAMBLE, DEFAULT_MAX_BLOB_BYTES, DEFAULT_SERVER_ALIAS, DEFAULT_TTL_MS, ENVELOPE_PREFIX, EXAMPLE_DENIAL_FALLBACK, EXAMPLE_HAPPY_PATH, Introspector, LIB_VERSION, MAX_CAPS, MCP_APP_MIME_TYPE, MRTR_PROTOCOL_REVISION, POLICY_DENIED_REASON, ProjectionError, RESOURCE_URIS, TOOLS_MODULE_SPECIFIER, TOOL_CALL_HOST_GLOBAL, XSTATE_VERSION, acceptedContent, buildBlobPayload, buildElicitationCopy, buildInstructions, buildResources, capLength, clampCaps, createIntrospector, createSandboxSession, decidePolicy, discoverApis, executeMachine, findTool, generateToolTypes, hashCode, inputRequired, inputResponse, isInputRequiredResult, isRecord, neutralizeControlChars, openBlob, policyDeniedReason, resolveCallPolicy, runSegment, schemaRequiresArgs, schemaToTsType, sealBlob, supportsMrtr, toStructuredError, wrapMcpServer };
2928
3155
 
2929
3156
  //# sourceMappingURL=index.mjs.map