@astral-mcp/projection 0.2.0 → 0.4.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.4.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
@@ -410,6 +410,19 @@ interface ExecuteMachineDeps {
410
410
  * `McpRequestContext.era`; `undefined` is treated as legacy.
411
411
  */
412
412
  protocolEra?: ProtocolEra;
413
+ /**
414
+ * Opt-in legacy-client compatibility (R28 amendment): when true, a
415
+ * suspension on a pre-2026-07-28 connection is returned as a normal
416
+ * `input_required` result and the SDK's serving layer fulfils it through
417
+ * old-style server→client `elicitation/create` requests, re-invoking this
418
+ * adapter with a byte-exact `requestState` echo and the collected
419
+ * `inputResponses` — the same retry envelope as a real MRTR retry, so
420
+ * every sealing/verification invariant runs unchanged. Cost: the
421
+ * `tools/call` stays open on this instance while the human answers, so
422
+ * statelessness (R27) is suspended for that call (free over stdio; needs
423
+ * session affinity over legacy HTTP). Default false: fail fast per R28.
424
+ */
425
+ legacyElicitation?: boolean;
413
426
  }
414
427
  /**
415
428
  * Run `execute_machine` for one `tools/call`, whether it is a first attempt
@@ -435,6 +448,8 @@ interface ProjectionResource {
435
448
  description: string;
436
449
  mimeType: string;
437
450
  text: string;
451
+ /** Resource-level metadata (e.g. the MCP Apps `ui` object). */
452
+ _meta?: Record<string, unknown>;
438
453
  }
439
454
  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
455
  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";
@@ -464,6 +479,17 @@ interface WrapOptions {
464
479
  redact?: RedactionHook;
465
480
  /** Replaces the library's code-mode preamble (R29). */
466
481
  codeModePreamble?: string;
482
+ /** Compatibility switches for pre-2026-07-28 clients. */
483
+ compat?: {
484
+ /**
485
+ * Serve approval suspensions to legacy clients through the SDK's
486
+ * server→client `elicitation/create` shim instead of failing fast
487
+ * (R28 amendment; DECISIONS D6). The client must declare the
488
+ * `elicitation` capability. Statelessness is suspended for the open
489
+ * call — free over stdio, needs session affinity over legacy HTTP.
490
+ */
491
+ legacyElicitation?: boolean;
492
+ };
467
493
  /** Identity the servers built by `createServer` advertise. */
468
494
  serverInfo?: Implementation;
469
495
  }
@@ -508,6 +534,40 @@ interface ProjectionFacade {
508
534
  */
509
535
  declare function wrapMcpServer(source: McpServer | Client, options: WrapOptions): Promise<ProjectionFacade>;
510
536
  //#endregion
537
+ //#region src/apps/dashboard.d.ts
538
+ /**
539
+ * MCP Apps workflow dashboard (PRD §12.1, second fenced use case).
540
+ *
541
+ * A static, library-authored HTML app that renders an `execute_machine`
542
+ * result — output, approval audit trail, console logs, or the structured
543
+ * error — inside the host's sandboxed iframe, per the MCP Apps extension
544
+ * (SEP-1865, spec 2026-01-26).
545
+ *
546
+ * Security posture (the §12.1 design constraint, satisfied by construction):
547
+ * - The HTML is a compile-time constant. No model-authored code, no
548
+ * template interpolation, no VM: UI rendering can never reach the tools
549
+ * bridge because nothing dynamic executes server-side at all.
550
+ * - Model-controlled data (outputs, audit args, log lines, error text)
551
+ * enters the DOM exclusively through `textContent` — the app never uses
552
+ * innerHTML — so hostile strings render inert (S6 extended to the app).
553
+ * - The app requests no host capabilities: `appCapabilities: {}`. It cannot
554
+ * call tools, open links, or write context; it only listens.
555
+ *
556
+ * Wire contract implemented (app side):
557
+ * - `ui/initialize` request → await result → `ui/notifications/initialized`.
558
+ * - Listens for `ui/notifications/tool-input`, `ui/notifications/tool-result`
559
+ * and `ui/notifications/tool-cancelled`; ignores everything else.
560
+ * - The result payload is the standard `CallToolResult` from
561
+ * `execute_machine`: `structuredContent` is either
562
+ * `{ output, approvals, logs }` or (with `isError`) the structured error
563
+ * `{ phase, message, hint, logs }`.
564
+ */
565
+ /** URI the dashboard is served under. MCP Apps mandates the ui:// scheme. */
566
+ declare const DASHBOARD_RESOURCE_URI = "ui://projection/dashboard.html";
567
+ /** MIME type the MCP Apps spec mandates for app HTML resources. */
568
+ declare const MCP_APP_MIME_TYPE = "text/html;profile=mcp-app";
569
+ 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";
570
+ //#endregion
511
571
  //#region src/errors.d.ts
512
572
  /**
513
573
  * Error carrying the structured `isError` body every failure returns (R25).
@@ -858,5 +918,5 @@ declare function runSegment(options: RunSegmentOptions): Promise<SegmentOutcome>
858
918
  */
859
919
  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
920
  //#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 };
921
+ 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
922
  //# 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.4.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.4.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;
@@ -2170,7 +2387,7 @@ function suspendResult(outcome, args, deps, caps) {
2170
2387
  hint,
2171
2388
  logs: outcome.logs
2172
2389
  });
2173
- if (!supportsMrtr(deps.protocolEra)) return fail("protocol", `this machine needs approval for a non-read-only tool call, which requires MCP protocol revision ${MRTR_PROTOCOL_REVISION} or later (this request is being served on the ${deps.protocolEra ?? "legacy"} protocol era)`, "Author a machine that only calls read-only tools, or connect with a client that negotiates the 2026-07-28 revision (SDK v2: `versionNegotiation: { mode: \"auto\" }`).");
2390
+ if (!supportsMrtr(deps.protocolEra) && deps.legacyElicitation !== true) return fail("protocol", `this machine needs approval for a non-read-only tool call, which requires MCP protocol revision ${MRTR_PROTOCOL_REVISION} or later (this request is being served on the ${deps.protocolEra ?? "legacy"} protocol era)`, "Author a machine that only calls read-only tools, or connect with a client that negotiates the 2026-07-28 revision (SDK v2: `versionNegotiation: { mode: \"auto\" }`).");
2174
2391
  if (outcome.trip > caps.maxRoundTrips) return fail("terminal", `this workflow reached the limit of ${caps.maxRoundTrips} approval round trips`, `Restructure the machine to need at most ${caps.maxRoundTrips} approval round trips.`);
2175
2392
  const inputRequests = {};
2176
2393
  for (const [key, call] of Object.entries(outcome.pending)) {
@@ -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,
@@ -2850,7 +3075,8 @@ var Facade = class {
2850
3075
  ...options.caps !== void 0 ? { caps: options.caps } : {},
2851
3076
  ...options.policy !== void 0 ? { policy: options.policy } : {},
2852
3077
  ...options.redact !== void 0 ? { redact: options.redact } : {},
2853
- ...era !== void 0 ? { protocolEra: era } : {}
3078
+ ...era !== void 0 ? { protocolEra: era } : {},
3079
+ ...options.compat?.legacyElicitation === true ? { legacyElicitation: true } : {}
2854
3080
  });
2855
3081
  }
2856
3082
  discoverApis() {
@@ -2866,7 +3092,10 @@ var Facade = class {
2866
3092
  const server = new McpServer(this.deps.options.serverInfo ?? {
2867
3093
  name: `projection-${this.deps.catalog.alias}`,
2868
3094
  version: "0.1.0"
2869
- }, { instructions: this.instructions });
3095
+ }, {
3096
+ instructions: this.instructions,
3097
+ inputRequired: { maxRounds: clampCaps(this.deps.options.caps ?? {}).maxRoundTrips }
3098
+ });
2870
3099
  this.registerOn(server, ctx);
2871
3100
  return server;
2872
3101
  }
@@ -2875,7 +3104,8 @@ var Facade = class {
2875
3104
  server.registerTool("execute_machine", {
2876
3105
  title: "Execute a state machine",
2877
3106
  description: EXECUTE_MACHINE_DESCRIPTION,
2878
- inputSchema: EXECUTE_MACHINE_INPUT_SCHEMA
3107
+ inputSchema: EXECUTE_MACHINE_INPUT_SCHEMA,
3108
+ _meta: { ui: { resourceUri: DASHBOARD_RESOURCE_URI } }
2879
3109
  }, async (args, ctx) => await this.executeMachine({
2880
3110
  code: args.code,
2881
3111
  ...args.lang !== void 0 ? { lang: args.lang } : {},
@@ -2890,7 +3120,8 @@ var Facade = class {
2890
3120
  }, () => this.discoverApis());
2891
3121
  for (const resource of this.resources) server.registerResource(resource.name, resource.uri, {
2892
3122
  description: resource.description,
2893
- mimeType: resource.mimeType
3123
+ mimeType: resource.mimeType,
3124
+ ...resource._meta !== void 0 ? { _meta: resource._meta } : {}
2894
3125
  }, () => ({ contents: [{
2895
3126
  uri: resource.uri,
2896
3127
  mimeType: resource.mimeType,
@@ -2924,6 +3155,6 @@ const EXECUTE_MACHINE_INPUT_SCHEMA = z.object({
2924
3155
  input: z.unknown().optional().describe("Passed to the machine as its `input`.")
2925
3156
  });
2926
3157
  //#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 };
3158
+ 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
3159
 
2929
3160
  //# sourceMappingURL=index.mjs.map