源码快照 @ 2026-09-02 (source-only, 剔除非源码素材)
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
# Creation-owned Automation runtime placement
|
||||
|
||||
## Outcome
|
||||
|
||||
The surface that creates an Automation owns its execution placement for the
|
||||
Automation's lifetime:
|
||||
|
||||
- Desktop creation produces a `desktop` Automation and continues to use the
|
||||
authenticated desktop runner introduced by the existing Automations work.
|
||||
- Web and Cloud Chat creation produce a `cloud` Automation. Den schedules the
|
||||
occurrence, wakes the owner's existing OpenWork Cloud container when it is
|
||||
stopped, and runs a native OpenWork thread headlessly inside that container.
|
||||
|
||||
Both surfaces read the same Den Automation and run history. Placement is shown
|
||||
on list cards and receipts, but it is not an editable setting. Moving execution
|
||||
between Desktop and Cloud requires creating a new Automation on that surface.
|
||||
|
||||
## Cloud lifecycle
|
||||
|
||||
A Cloud agent run reserves and persists its worker, workspace, native thread,
|
||||
and deterministic user-message identity before it submits the prompt. Lease
|
||||
recovery reattaches to that thread and observes-or-submits that exact message
|
||||
instead of starting duplicate work. Den heartbeats the run while it waits,
|
||||
requires an observed idle state after forwarding cancellation, records native
|
||||
assistant/tool events and usage, and keeps active Cloud runs out of idle-stop
|
||||
selection. Run deadlines include container wake-up and request admission.
|
||||
|
||||
Cloud creation requires the current owner to have an existing per-user Cloud
|
||||
worker and an authorized model. It does not silently allocate a new Cloud
|
||||
environment. A stopped worker is valid and is woken at execution time; a
|
||||
missing or failed runtime is recorded as a durable failure and moves the
|
||||
Automation to needs-attention. Timeouts are terminal receipts rather than
|
||||
automatic retries because an agent may already have produced external side
|
||||
effects; the user can inspect the native run and choose Run now explicitly.
|
||||
Runtime readiness polling retains its pre-existing 120-second cap and terminal,
|
||||
non-retryable outcome; signed-preview routing does not add another queued attempt.
|
||||
|
||||
Before prompt admission, Den probes and repairs the worker's persisted
|
||||
`openwork-cloud` MCP registration for the selected model. A missing Connect
|
||||
configuration or model projection becomes durable needs-attention rather than
|
||||
a headless run that silently lacks the user's connected services.
|
||||
|
||||
The native thread client uses both the collaborator token and the internal
|
||||
host credential when it talks directly to the resolved Cloud runtime. No provider
|
||||
credential or worker token is copied into the Automation receipt.
|
||||
|
||||
## Relationship to Workflow Automations
|
||||
|
||||
Workflow Automations from the Workflow Artifacts work remain Cloud-owned
|
||||
and keep their existing Den Code Mode executor. This feature adds ordinary
|
||||
agent actions as a second Cloud execution kind; it does not change Script
|
||||
snapshot validation or artifact result retention.
|
||||
|
||||
## Verification boundary
|
||||
|
||||
Focused tests cover the Cloud-only MCP creation contract, deterministic prompt
|
||||
admission, terminal errors, cancellation observation, dual worker credentials,
|
||||
Cloud wake/idle lifecycle behavior, runtime-specific agent guidance, and
|
||||
cross-surface execution labels. Den Web provides once/daily/weekly create and
|
||||
edit flows plus activate/deactivate, archive, run-now, cancel, native thread,
|
||||
usage, and event receipt controls. The testkit acceptance tape covers the
|
||||
Web/Desktop creation language and verifies that no placement picker is exposed.
|
||||
|
||||
The deployment-shaped Daytona journey—stop a real user's container, let a due
|
||||
occurrence wake it, execute against live model and Connect configuration, then
|
||||
observe idle shutdown again—remains required for the production rollout and
|
||||
merge decision. It is intentionally not claimed by local or mocked proof.
|
||||
@@ -0,0 +1,51 @@
|
||||
# Desktop runner for Den-scheduled Automations
|
||||
|
||||
## Outcome
|
||||
|
||||
Den remains the durable scheduler and source of truth for Automation schedules,
|
||||
occurrences, leases, notification cursors, ordered events, usage, and terminal
|
||||
results. Den no longer launches OpenCode or stores an Automation execution
|
||||
runtime in its container.
|
||||
|
||||
An authenticated desktop installation registers as a `desktop` runner and
|
||||
maintains a Server-Sent Events stream. SSE carries only a wake-up type and a
|
||||
resumable notification cursor. After connecting or receiving a wake-up, the
|
||||
desktop discovers work over HTTP, atomically claims one occurrence, and reports
|
||||
attempt-bound heartbeats, ordered events, cancellation, usage, and completion.
|
||||
|
||||
When a runner has no pending notifications, Den exponentially backs its cursor
|
||||
query off from one second to 15 seconds. The poll resets to one second after
|
||||
activity, and its sleep is capped by the existing 15-second presence keepalive.
|
||||
This preserves the resumable SSE and bounded claim protocol while reducing an
|
||||
idle runner's steady-state notification queries from 60 to four per minute.
|
||||
|
||||
The claimed Automation runs as a normal visible local OpenWork thread in the
|
||||
desktop's active workspace. It uses the selected model and the same local
|
||||
OpenCode tool and integration experience as a thread started by the user.
|
||||
|
||||
## Offline behavior
|
||||
|
||||
Scheduled desktop occurrences have a bounded claim window. If no eligible
|
||||
desktop claims one before its deadline, Den durably records `skipped` with
|
||||
`runner_unavailable`; the app displays **Missed — desktop runner unavailable**.
|
||||
Run now fails immediately with **No desktop runner is online** while no desktop
|
||||
SSE connection is present.
|
||||
|
||||
## Focused proof
|
||||
|
||||
The 2026-08-04 packaged macOS candidate was connected to the local Den test
|
||||
profile and produced both required outcomes:
|
||||
|
||||
- `Desktop visible thread E2E 08:45:31` succeeded as a normal sidebar thread,
|
||||
with result `DESKTOP_VISIBLE_THREAD_OK`, ordered user/assistant/usage/terminal
|
||||
events, and token usage stored in Den.
|
||||
- `Disconnected desktop final 08:46:51` was not claimed and became the explicit
|
||||
missed desktop-runner-unavailable receipt. Run now while disconnected returned
|
||||
the explicit no-runner-online response.
|
||||
|
||||
## Deferred hardening
|
||||
|
||||
Repository-wide suites, cross-platform packaged proof, multi-replica online
|
||||
presence, exhaustive reconnect/race testing, fault injection, and broader
|
||||
security review remain follow-up work. A future `sandbox` target can extend the
|
||||
execution-target contract; it is not implemented here.
|
||||
@@ -0,0 +1,91 @@
|
||||
# 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.
|
||||
@@ -0,0 +1,52 @@
|
||||
# Local managed MCP OAuth
|
||||
|
||||
OpenWork desktop can own OAuth for a custom remote MCP and expose its tools to
|
||||
the bundled OpenCode engine through an authenticated loopback MCP gateway. This
|
||||
provides a compatibility path for providers whose OAuth flow works through
|
||||
OpenWork's enterprise MCP client but not through OpenCode's direct MCP client.
|
||||
|
||||
## User flow
|
||||
|
||||
1. In a local desktop workspace, add a remote MCP and expand **OpenWork-managed
|
||||
OAuth**.
|
||||
2. Optionally enter a pre-registered client ID, client secret, and scopes. If
|
||||
the provider supports dynamic client registration, those fields may remain
|
||||
empty.
|
||||
3. OpenWork performs OAuth discovery, DCR when needed, PKCE authorization, the
|
||||
loopback callback, token exchange, and an authenticated `tools/list` check.
|
||||
4. OpenCode receives a remote MCP entry pointing at the OpenWork loopback
|
||||
gateway with `oauth: false`. It sees the provider tools but never receives
|
||||
the provider access token, refresh token, or OAuth client secret.
|
||||
|
||||
Existing direct remote MCPs and local command MCPs keep their current paths.
|
||||
The managed path is opt-in per connection and is currently desktop-only.
|
||||
|
||||
## Persistence and lifecycle
|
||||
|
||||
- Provider credentials, OAuth registrations, discovery state, and PKCE
|
||||
transactions are encrypted with AES-256-GCM in OpenWork's runtime storage.
|
||||
- OpenWork Desktop keeps the encryption key behind the operating system's
|
||||
secure-storage service and persists only the protected key blob, separately
|
||||
from the encrypted vault. A standalone server must set
|
||||
`OPENWORK_ENCRYPTION_KEY`; there is no plaintext key-file fallback.
|
||||
- The gateway bearer is scoped to a workspace and connection, generated from a
|
||||
process-only secret, and rotated on every OpenWork server restart.
|
||||
- Startup reconciliation rewrites managed runtime MCP entries with the current
|
||||
loopback port and bearer while keeping the encrypted provider credential.
|
||||
- Disconnect deletes the stored credential and disables the gateway entry.
|
||||
|
||||
## Network boundary
|
||||
|
||||
Outside explicit local development, managed providers must use HTTPS and
|
||||
resolve only to public addresses. Redirects are revalidated, HTTPS downgrade is
|
||||
blocked, and credential-bearing request bodies cannot cross origins. This
|
||||
prevents a configured MCP URL or redirect from turning the desktop gateway into
|
||||
an internal-network request proxy.
|
||||
|
||||
## Verification
|
||||
|
||||
Focused server coverage proves discovery and DCR, PKCE authorization, encrypted
|
||||
credential persistence, OpenCode runtime registration, gateway tool listing and
|
||||
invocation, refresh-token recovery, restart reconciliation with bearer
|
||||
rotation, disconnect, and the outbound URL guard. The desktop and server
|
||||
TypeScript projects and the compiled embedded server build are also checked.
|
||||
@@ -0,0 +1,61 @@
|
||||
# MCP Apps inline host
|
||||
|
||||
OpenWork Desktop can render the standard MCP Apps UI attached to a completed
|
||||
MCP tool call. The normal text result remains visible and usable if the view is
|
||||
absent, unsupported, or fails to initialize.
|
||||
|
||||
## Runtime flow
|
||||
|
||||
1. OpenCode calls the configured MCP tool once.
|
||||
2. OpenWork's bundled engine plugin preserves that call's standard `content`,
|
||||
`structuredContent`, and result `_meta` fields in the completed tool part.
|
||||
3. The desktop asks its local OpenWork server to find the original projected
|
||||
tool on the configured MCP server.
|
||||
4. The server advertises the `io.modelcontextprotocol/ui` extension, reads the
|
||||
tool's `_meta.ui.resourceUri`, and resolves the `ui://` resource through
|
||||
`resources/read`.
|
||||
5. The conversation mounts the returned `text/html;profile=mcp-app` resource in
|
||||
an opaque sandboxed iframe and delivers the original tool input and result
|
||||
over the official MCP Apps bridge.
|
||||
|
||||
The host never replays the originating tool call to reconstruct structured
|
||||
data. MCP tools that return only standard text content can still attach a view.
|
||||
|
||||
## Security boundary
|
||||
|
||||
- Resource markup runs in an opaque `sandbox="allow-scripts"` iframe with no
|
||||
referrer, forms, popups, same-origin access, top navigation, downloads, or
|
||||
device permissions.
|
||||
- The host injects a deny-by-default CSP before resource markup. It accepts at
|
||||
most 16 fixed HTTPS origins per declared source list, with loopback HTTP
|
||||
allowed for local development.
|
||||
- Remote MCP connections must use HTTPS, except for loopback HTTP. Configured
|
||||
MCP headers and credentials remain in the local server and are never exposed
|
||||
to the iframe.
|
||||
- Resource HTML is limited to 512 KiB, proxied tool results to 1 MiB, and tool
|
||||
discovery to 2,048 tools across at most 32 pages.
|
||||
- A view may call only tools from its originating configured MCP server. The
|
||||
target must be visible to apps, explicitly `readOnlyHint: true`, not
|
||||
destructive, and allowed by the workspace's MCP tool policy.
|
||||
- Dedicated sandbox origins, camera, microphone, geolocation, clipboard
|
||||
access, sampling, host messaging, external link opening, downloads, and
|
||||
write-capable tool calls are not granted by this host slice.
|
||||
|
||||
## Current compatibility
|
||||
|
||||
The resource resolver currently supports configured remote Streamable HTTP MCP
|
||||
servers, with SSE fallback for legacy remote servers. It does not yet resolve
|
||||
resources directly from command/stdio MCP entries. OpenWork-managed OAuth
|
||||
connections also require a follow-up adapter so resource discovery can reuse
|
||||
their encrypted server-side credential path; ordinary remote connections with
|
||||
configured server-side headers work today.
|
||||
|
||||
## Verification
|
||||
|
||||
Focused server tests cover extension negotiation, projected tool naming,
|
||||
resource resolution, tool visibility, read-only same-server mediation, and
|
||||
write rejection. App tests cover result preservation, session mapping, CSP
|
||||
construction, and safe HTML injection. The Testkit scenario
|
||||
`mcp-app-inline-host.e2e.test.ts` drives a deterministic OpenCode tool call,
|
||||
resolves the declared `ui://` resource, mounts the sandboxed view, and captures
|
||||
the visible inline card and fallback transcript.
|
||||
@@ -0,0 +1,138 @@
|
||||
# Native MCP Apps
|
||||
|
||||
OpenWork supports MCP Apps delivered by standard MCP servers connected through
|
||||
OpenWork Connect. A server advertises the stable
|
||||
`io.modelcontextprotocol/ui` extension, a tool binds an exact UI resource with
|
||||
`_meta.ui.resourceUri`, the host reads that resource with `resources/read`, and
|
||||
tool inputs and results move over the standard MCP Apps bridge.
|
||||
|
||||
This unit of value does **not** include installing a standalone App from an
|
||||
HTML URL. URL-imported MCP Apps are deferred future work with a separate
|
||||
product, security, lifecycle, and rollout contract.
|
||||
|
||||
Workflows remain executable `workflow` config objects. A Workflow's generated
|
||||
views can be MCP resources, but that does not turn Workflow execution into
|
||||
resource loading or a standalone URL-App installation path.
|
||||
|
||||
## Standard MCP server path
|
||||
|
||||
Connect continues to own server configuration, authentication, access grants,
|
||||
per-member credentials, and tool policy. The OpenWork Cloud control server
|
||||
publishes a member-scoped resource at:
|
||||
|
||||
```text
|
||||
openwork://connect/mcp-servers/index.json
|
||||
```
|
||||
|
||||
The signed-in Desktop session mints a separate short-lived App-host credential
|
||||
with a non-public `mcp:app-host` scope. Desktop stores that credential and the
|
||||
endpoint descriptors only in private App-host state; neither is projected into
|
||||
OpenCode. It reads the index with that credential and advertises the
|
||||
`mcp-app-host-v1` client capability. Den returns a non-empty provider index only
|
||||
when the server-verified scope, client capability, and both rollout gates are
|
||||
present. A normal model or legacy MCP token cannot unlock the index by spoofing
|
||||
an audience or capability header. Desktop never writes `openwork-connect-*`
|
||||
entries to the OpenCode runtime or any model-visible MCP registry. A connection
|
||||
is proxied at:
|
||||
|
||||
```text
|
||||
/mcp/agent/connections/{connectionId}
|
||||
```
|
||||
|
||||
Current Desktop clients never register these provider descriptors in OpenCode;
|
||||
the model discovers and invokes ordinary provider operations only through the
|
||||
central `openwork-cloud` `search_capabilities` and `execute_capability` tools.
|
||||
For stale published clients that still retain an old per-connection entry, the
|
||||
proxy exposes only a compatibility pair named `search_capabilities` and
|
||||
`execute_capability`. It never returns the provider catalog, MCP App launch
|
||||
tools or metadata, resources, or templates, and it rejects every direct
|
||||
provider call. This keeps ordinary operations bounded while old entries are
|
||||
removed during reconciliation.
|
||||
|
||||
Desktop's local App host authenticates with the private scoped credential. Only
|
||||
that transport receives tools with a valid `_meta.ui.resourceUri` whose
|
||||
provider-declared visibility includes `app`, plus app-visible `search_capabilities` and
|
||||
`execute_capability` scoped to the originating server. A native MCP App can
|
||||
therefore render and use authorized tools from its regular MCP server without
|
||||
placing the provider catalog in the model request.
|
||||
|
||||
The app-host view preserves:
|
||||
|
||||
- the App tool's exact name, input/output schemas, annotations, and UI binding;
|
||||
- concrete descriptors and `resources/read` content only for resources bound
|
||||
by exposed App tools;
|
||||
- `content`, `structuredContent`, `_meta`, and `isError` from `tools/call`;
|
||||
- the stable MCP Apps extension and `text/html;profile=mcp-app` resources;
|
||||
- one server identity per Connect connection, preserving the same-server
|
||||
tool-call boundary.
|
||||
|
||||
OpenWork access grants, disabled-tool policy, and approval rules still apply at
|
||||
the proxy boundary. The App-host credential authorizes only this bounded proxy
|
||||
surface; it is not a provider credential and grants no direct cross-server
|
||||
access.
|
||||
|
||||
## Rollout
|
||||
|
||||
Native MCP Apps are enabled for every deployment and organization. The former
|
||||
deployment gate (`DEN_REMOTE_MCP_APPS_ENABLED`) and the per-organization
|
||||
**Native MCP Apps (preview)** capability were removed once the feature
|
||||
stabilized; stale stored organization overrides are ignored. App launch
|
||||
metadata (`kind: mcp_app`, `mcpApp.resourceUri`, `openwork/mcpApp` meta) is an
|
||||
opaque binding published on every bounded search/execute result; clients that
|
||||
do not host Apps ignore it and keep the normal tool result. The private
|
||||
App-host index and per-connection provider proxy remain gated on the
|
||||
client-advertised App-host capability header, so older Desktop clients keep
|
||||
their bounded search/execute surface. Reconciliation also removes and
|
||||
disconnects stale
|
||||
`openwork-connect-*` OpenCode entries while preserving user-authored MCPs and
|
||||
all durable Connect records.
|
||||
|
||||
The provider proxy advertises `listChanged: false` because the current
|
||||
enterprise connector opens bounded request sessions rather than a durable
|
||||
downstream notification stream. Catalog refresh happens on Connect
|
||||
reconciliation, Desktop startup, engine refresh, or an explicit Cloud MCP
|
||||
refresh. Forwarding downstream list-change notifications remains follow-up
|
||||
interoperability work.
|
||||
|
||||
## Deferred: standalone URL-imported Apps
|
||||
|
||||
Installing a self-contained HTML App from a URL is intentionally outside this
|
||||
change. In the current product:
|
||||
|
||||
- Den Web has no Add MCP App button, URL form, installed-App detail page, or
|
||||
URL-App lifecycle entry point;
|
||||
- the central MCP server does not register `import_remote_mcp_app` or any
|
||||
standalone-App launch tool;
|
||||
- capability search returns no standalone URL-App matches;
|
||||
- model and App-host catalogs contain no standalone URL-App tools;
|
||||
- no `ui://openwork/library-apps/...` resources are registered;
|
||||
- member server indexes and launch metadata contain no standalone URL Apps;
|
||||
- REST calls under `/v1/remote-mcp-apps` are not registered and are therefore
|
||||
unavailable.
|
||||
|
||||
Existing database rows and cached revisions from earlier development remain
|
||||
stored non-destructively. They are inactive and unreachable through the UI,
|
||||
MCP catalogs, capability search, resources, launch metadata, and HTTP API. This
|
||||
change performs no deletion and introduces no destructive migration.
|
||||
|
||||
The retained storage and validation implementation is not a supported runtime
|
||||
surface. A future standalone URL-App unit of value must deliberately restore
|
||||
its own API, UI, security review, lifecycle, testing, and rollout contract.
|
||||
|
||||
## Host security and compatibility
|
||||
|
||||
Desktop negotiates the stable extension, resolves the current tool definition,
|
||||
reads the exact `ui://` resource even when it is absent from `resources/list`,
|
||||
accepts text or base64 HTML, enforces MIME and size limits, validates CSP
|
||||
origins, and loads the document through the isolated sandbox proxy. It sends
|
||||
tool input and the preserved tool result after initialization, bounds size
|
||||
changes, tears the bridge down on unmount, and contains resolution, handshake,
|
||||
document, and runtime failures without hiding the normal tool result.
|
||||
|
||||
App-requested tools are resolved only on the originating regular MCP server
|
||||
and must be visible to the app. When a tool carries `_meta.ui.resourceUri`,
|
||||
Desktop also requires it to match the exact resource loaded in the calling
|
||||
iframe. Workspace denies apply. Read-only capability search runs directly;
|
||||
capability execution uses conservative mutation annotations and requires user
|
||||
confirmation while provider authorization and audit still run server-side.
|
||||
Cross-server iframe calls are not allowed.
|
||||
@@ -0,0 +1,110 @@
|
||||
# Native MCP Apps local demo
|
||||
|
||||
This runbook proves the native MCP Apps path with a deterministic local fixture.
|
||||
It does not depend on a hosted gallery, production rollout flags, customer
|
||||
connectors, or personal credentials.
|
||||
|
||||
The required fixture is Project Atlas from
|
||||
`evals/specs/remote-mcp-apps.e2e.test.ts`. It runs as a local Streamable HTTP MCP
|
||||
server, exposes `ui://project-atlas/view.html`, and has a same-server
|
||||
`search_projects` operation. The tape starts real local Den API and Web
|
||||
processes, a real Electron Desktop/local-server process, MySQL, Redis, and a
|
||||
synthetic model server.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Node.js 24 and pnpm 11.4.0 (the versions declared by the repository)
|
||||
- Bun, Docker, and an available local MySQL port (`3306`)
|
||||
- no real model/provider credentials in the shell
|
||||
|
||||
From a clean checkout of the exact branch under test:
|
||||
|
||||
```bash
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm --dir evals install --frozen-lockfile
|
||||
pnpm --filter @openwork/types build
|
||||
pnpm --filter @openwork-ee/den-db build
|
||||
pnpm --filter @openwork/email build
|
||||
pnpm dev:den:mysql
|
||||
```
|
||||
|
||||
## Run the exact-head demo tape
|
||||
|
||||
```bash
|
||||
OPENWORK_EVAL_E2E_TESTS=1 pnpm evals:e2e remote-mcp-apps
|
||||
```
|
||||
|
||||
A valid required proof ends with one passing test, zero failed tests, zero
|
||||
skips, and `"verdict":"passed"`. Keep the JSON report path printed by the
|
||||
runner with the local verification record.
|
||||
|
||||
The tape performs this demo sequence:
|
||||
|
||||
1. Starts an isolated Den organization, synthetic member, local Project Atlas
|
||||
MCP server, and Desktop profile.
|
||||
2. Adds Project Atlas as an organization Connect MCP server and waits for it to
|
||||
become ready.
|
||||
3. Confirms the model-visible surface contains only the central
|
||||
`search_capabilities` and `execute_capability` tools.
|
||||
4. Confirms stale per-connection compatibility exposes only that same bounded
|
||||
pair, no resources/templates/App metadata, and no direct provider tools.
|
||||
5. Confirms the private App host receives only the originating server's
|
||||
app-visible tools and exact `ui://project-atlas/view.html` resource.
|
||||
6. Searches and executes `search_projects` through the originating server's
|
||||
App-host capability pair and observes the synthetic Atlas migration result.
|
||||
7. Prompts the synthetic model to return the bound tool result, renders the App
|
||||
in Desktop, reloads it, and confirms it renders again.
|
||||
8. Restarts Desktop with the same isolated profile, revisits the session, and
|
||||
confirms the App recovers.
|
||||
|
||||
Native MCP Apps are enabled for every deployment and organization; no
|
||||
deployment or organization gate needs to be flipped for this demo.
|
||||
|
||||
## Focused security and compatibility checks
|
||||
|
||||
```bash
|
||||
cd apps/server
|
||||
bun --conditions=development test \
|
||||
src/connect-mcp-server-catalog.test.ts \
|
||||
src/mcp-app-host.test.ts \
|
||||
src/mcp-app-sandbox.test.ts \
|
||||
src/cloud-mcp-reconcile.e2e.test.ts
|
||||
|
||||
cd ../app
|
||||
bun test \
|
||||
tests/den-mcp-url.test.ts \
|
||||
tests/mcp-app-frame.test.ts \
|
||||
tests/session-mcp-maintenance.test.ts \
|
||||
tests/cloud-mcp-maintenance-gate.test.ts
|
||||
|
||||
cd ../../ee/apps/den-api
|
||||
bun test \
|
||||
test/remote-mcp-app-rollout.test.ts \
|
||||
test/external-connection-proxy.test.ts \
|
||||
test/admin-organization-capabilities.test.ts \
|
||||
test/organization-capabilities.test.ts
|
||||
```
|
||||
|
||||
These checks cover exact trusted-origin matching, cross-origin rejection,
|
||||
origin-bound App-host credentials, App-host credential privacy, sandbox/CSP
|
||||
enforcement, same-server binding, rollout defaults, older responses without an
|
||||
App-host credential, bounded stale-client compatibility, and preservation of
|
||||
ordinary Connect when Apps are disabled.
|
||||
|
||||
## Expected failure categories
|
||||
|
||||
- A skipped tape is incomplete proof. Check the consent environment variable,
|
||||
local placement, and MySQL availability.
|
||||
- `missing local server credentials` is a Desktop startup/readiness problem.
|
||||
- A resource error names the exact URI, MIME, size, or CSP validation failure.
|
||||
- An origin error indicates that the Den/App-host endpoint pair is not an exact
|
||||
trusted-origin match.
|
||||
- A tool denial indicates wrong-server routing, app visibility, workspace
|
||||
policy, provider authorization, or required mutation confirmation.
|
||||
- A client that does not advertise the private App-host capability keeps the
|
||||
bounded search/execute surface; that is expected, not a resource-loading
|
||||
failure.
|
||||
|
||||
The hosted SOL gallery may be checked separately as an external observation,
|
||||
but its reachability is not OpenWork compatibility proof and it is not part of
|
||||
this required demo.
|
||||
Reference in New Issue
Block a user