| 1 | # MCP 2026 capability surface |
| 2 | |
| 3 | Reasonix speaks the MCP `2026-07-28` protocol revision (multi-round-trip |
| 4 | requests, form/URL elicitation) and the stable MCP Apps `2026-01-26` |
| 5 | extension on Desktop. Users change nothing: servers keep their existing |
| 6 | enable/disable switches, and every new capability rides the frontend's |
| 7 | host profile. |
| 8 | |
| 9 | ## Host capability profiles |
| 10 | |
| 11 | | Frontend | Profile | Declares | |
| 12 | |---|---|---| |
| 13 | | CLI headless (`-p`), bots | `core-v1` | legacy surface only (byte-identical to the old client) | |
| 14 | | CLI chat TUI, `serve` | `interactive-v1` | + form and URL elicitation | |
| 15 | | Desktop | `desktop-apps-2026-01-26-v1` | + elicitation and `io.modelcontextprotocol/ui` (`text/html;profile=mcp-app`) | |
| 16 | |
| 17 | The profile is fixed when the host is created. If the Apps sandbox |
| 18 | listener cannot bind, Desktop degrades to `interactive-v1` before the |
| 19 | first connection — MCP Core and text results stay available and no |
| 20 | server is told this client renders apps. |
| 21 | |
| 22 | `/mcp status` (TUI), the Desktop MCP panel, and `control.MCPCapabilityViews` |
| 23 | expose the four-layer matrix — **Protocol Connection / Core Host / |
| 24 | Interactive Host / Apps Host** — with `supported | negotiated | degraded | |
| 25 | unavailable` states derived from live sessions. There is no editable |
| 26 | switch. |
| 27 | |
| 28 | ## Elicitation (MRTR) |
| 29 | |
| 30 | A server may pause a `tools/call` with input requests (form schema or a |
| 31 | credential-free URL). The broker travels with the per-call context, so |
| 32 | the answer always reaches the tab or terminal that started the call: |
| 33 | |
| 34 | - **Desktop/serve/TUI**: a typed form (flat primitive schema: string, |
| 35 | number, integer, boolean, enum, defaults, required, bounds) or a URL |
| 36 | card showing the server and target origin; the browser opens only on |
| 37 | an explicit click. Submit / refuse / close map to accept / decline / |
| 38 | cancel. |
| 39 | - **Headless (no broker)**: the capability is not declared; a stray |
| 40 | request answers cancel — the model never guesses. |
| 41 | - Frontend reconnects replay a pending elicitation; process exit cancels |
| 42 | the underlying call. |
| 43 | - Form values and URL targets never enter logs or telemetry; decision |
| 44 | receipts record only kind and action. |
| 45 | |
| 46 | ## MCP Apps (Desktop) |
| 47 | |
| 48 | Tools may declare Apps metadata: `_meta.visibility` (`["model","app"]` |
| 49 | by default) and `_meta.ui.resourceUri` (nested key preferred, flat |
| 50 | `ui/resourceUri`/`resourceUri` accepted) with optional per-resource CSP. |
| 51 | App-only tools stay in a server-private catalog — invisible to the model |
| 52 | and to `use_capability list`. |
| 53 | |
| 54 | Results from App-capable tools carry a bounded local presentation (one |
| 55 | aggregate 512 KiB cap including metadata and JSON framing; inline |
| 56 | audio/video and oversized nested base64 are stripped) that is persisted |
| 57 | for the Desktop card and stripped from every provider request. |
| 58 | |
| 59 | Inline surfaces run in a double-iframe sandbox. A per-server loopback |
| 60 | origin relays AppBridge traffic in both directions to a sandboxed inner |
| 61 | frame, with parent/inner source checks, instance-nonce binding, an 8 MiB |
| 62 | UTF-8 frame cap, a 4 MiB `ui://` HTML cap, and deny-all CSP extended only |
| 63 | by exact declared origins. Opening a card validates its server, tool, |
| 64 | catalog generation, and resource URI, then freezes the resource in the |
| 65 | bounded live-instance registry. The SHA-256 digest is bound into the |
| 66 | resource request and response, so content cannot change within that App |
| 67 | instance. Reopening an older card creates a new validated snapshot of the |
| 68 | server's current resource; Reasonix does not persist executable App HTML |
| 69 | in the conversation. |
| 70 | |
| 71 | After `ui/notifications/initialized`, Desktop sends the original tool |
| 72 | input followed by the full bounded `CallToolResult`; unmount waits up to |
| 73 | one second for `ui/resource-teardown`. App-initiated `tools/call`, link |
| 74 | opening, resource loading, and cleanup remain bound to the originating |
| 75 | tab even if the user switches tabs. External `http(s)` links require one |
| 76 | confirmation per App instance and origin, and are validated again by the |
| 77 | native host. App tool calls resolve through the instance registry (same |
| 78 | server, app visibility, current catalog generation) and record nested, |
| 79 | local-only events — visible in the transcript, never added to model |
| 80 | context. |
| 81 | |
| 82 | ## Cache and cross-version compatibility |
| 83 | |
| 84 | Cache identity is the profile, never SDK version, time, or negotiation |
| 85 | results: |
| 86 | |
| 87 | | Scenario | Behavior | |
| 88 | |---|---| |
| 89 | | New Desktop first reads an old core cache | miss; the enhanced profile handshakes its own catalog | |
| 90 | | New CLI/serve | keep the interactive profile's own cache; never read the Desktop cache | |
| 91 | | Old binary writes | only the legacy `<slug>.json`; enhanced files untouched | |
| 92 | | New session opened by an old binary | `mcp_app` ignored; text results intact | |
| 93 | | Old binary rewrites a session | interaction replay metadata may be lost; text and pairing survive | |
| 94 | | Both writing one session | unsupported (existing single-writer/session-lock boundary) | |
| 95 | |
| 96 | `use_capability` keeps its name, schema, ordering, and lazy-connect |
| 97 | behavior byte-for-byte. App-only tools never enter provider requests, |
| 98 | and all cache files keep atomic writes with `0600` permissions. |
| 99 |