92 lines
4.4 KiB
Markdown
92 lines
4.4 KiB
Markdown
# Workflow Artifacts as MCP Apps
|
|
|
|
OpenWork exposes Workflow results as portable, standards-based
|
|
MCP Apps. A supporting MCP host can render an artifact inline; every other MCP
|
|
client still receives a useful Markdown result.
|
|
|
|
## Product flow
|
|
|
|
1. Code Mode produces data and a deterministic Markdown rendering.
|
|
2. The result can be saved as an immutable Workflow snapshot with a receipt,
|
|
schema validation, result digest, and exact Workflow version.
|
|
3. A person or an Automation may run the Workflow again. Automations only
|
|
refresh the data snapshot; they do not create or execute UI code.
|
|
4. The read-only `render_workflow_artifact` tool loads the latest successful
|
|
snapshot, or an exact receipt when requested.
|
|
`render_dynamic_artifact` remains as a deprecated alias for one release.
|
|
5. MCP Apps hosts resolve the linked `ui://` resource and inject the tool's
|
|
structured result. Other hosts show the Markdown fallback.
|
|
|
|
This keeps execution, scheduling, data, and presentation separate:
|
|
|
|
```text
|
|
Workflow ──run──> immutable snapshot ──read──> MCP tool result
|
|
▲ ▲ ├─ Markdown fallback
|
|
│ │ └─ ui:// MCP App
|
|
explicit run Automation refresh
|
|
```
|
|
|
|
The MCP App never runs a Workflow, mutates an artifact, or introduces another
|
|
scheduler. It only presents an already-authorized snapshot.
|
|
|
|
## MCP Apps conformance
|
|
|
|
The agent MCP server implements the `io.modelcontextprotocol/ui` extension and
|
|
the `2026-01-26` MCP Apps protocol:
|
|
|
|
- server capability: `extensions.io.modelcontextprotocol/ui.mimeTypes`
|
|
contains `text/html;profile=mcp-app`;
|
|
- tool metadata: `_meta.ui.resourceUri` points to
|
|
`ui://openwork/workflow-artifact/v1/view.html` (with the compatibility metadata
|
|
emitted by the official MCP Apps server helpers);
|
|
- resource delivery: `resources/read` returns one self-contained HTML5 document
|
|
with MIME type `text/html;profile=mcp-app`;
|
|
- view lifecycle: the document performs `ui/initialize`, sends
|
|
`ui/notifications/initialized`, receives tool input/result/cancellation and
|
|
host-context notifications, reports size changes, and acknowledges teardown;
|
|
- data delivery: the tool returns versioned `structuredContent`, lightweight
|
|
lineage in result `_meta`, and a complete text fallback;
|
|
- security: the resource declares an empty network/frame/base-URI CSP, loads no
|
|
external code, performs no network requests, and inserts artifact values with
|
|
DOM text APIs rather than HTML interpolation.
|
|
|
|
The shared payload schema lives in `@openwork/types/workflows` so a host
|
|
can validate the data contract independently of this presentation resource.
|
|
|
|
## UI behavior
|
|
|
|
The self-contained view is intentionally data-first and framework-neutral. It
|
|
adapts the Preview tab to common result shapes:
|
|
|
|
- arrays of records become a bounded table;
|
|
- flat objects become metric cards;
|
|
- nested or irregular values fall back to formatted JSON;
|
|
- Data always exposes the full structured result, with a rendering-size guard;
|
|
- Lineage shows the immutable receipt, Workflow/version IDs, source, digest,
|
|
renderer, and Automation run when present.
|
|
|
|
The view follows host theme/style context, uses responsive HTML, and caps large
|
|
tables and rendered JSON without changing the underlying tool result.
|
|
|
|
## Authorization and failure behavior
|
|
|
|
The renderer uses the same organization membership, team grants, and saved
|
|
Workflow authorization path as the existing artifact APIs. It does not accept a
|
|
plugin or result payload from the caller. The caller supplies only a saved
|
|
Workflow ID, an optional exact receipt, and an optional freshness threshold.
|
|
|
|
Only readable, successful, non-deleted snapshots can be rendered. Missing,
|
|
failed, deleted, or unauthorized results fail closed with a non-sensitive text
|
|
error. A stale snapshot remains renderable and is labeled stale; a failed
|
|
refresh leaves the last successful artifact available with a needs-attention
|
|
state.
|
|
|
|
## Next interoperable slice
|
|
|
|
The provider side is deliberately usable without an OpenWork-specific host.
|
|
The next slice is a generic MCP Apps host in the desktop conversation surface:
|
|
negotiate the UI extension with upstream MCP servers, preserve tool/resource
|
|
metadata and structured results through the runtime, sandbox `ui://` resources,
|
|
and bridge the standard JSON-RPC lifecycle. That host should consume the same
|
|
standard contract rather than adding a custom React artifact format.
|