Skip to main content
The apps conformance command validates the server-side MCP Apps surface your server exposes through tools and ui:// resources. Use tools call --ui when you want to execute one UI-capable tool and render the completed result in the local Inspector App Builder.
This is currently server-side MCP Apps conformance only. A passing run does not prove full SEP-1865 host behavior such as ui/initialize, ui/notifications/tool-input ordering, sandbox proxy behavior, or host display-mode handling.

Quick start

For a local stdio server:
You can emit CI-friendly JUnit XML from either the single-run or suite command:
To reproduce one tool result in the local Inspector:

What it checks

The current runner validates:
  1. MCP Apps tools are present.
  2. Tool metadata uses valid _meta.ui.resourceUri and visibility values.
  3. Tool inputSchema is a non-null JSON Schema object.
  4. Listed UI resources use ui:// URIs and text/html;profile=mcp-app.
  5. Referenced UI resources can be fetched with resources/read.
  6. Resource contents provide exactly one HTML payload via text or blob.
  7. _meta.ui.csp, permissions, domain, and prefersBorder use valid shapes.

Example output

Typical success summary:

Suite runner

Use apps conformance-suite when you want a named CI matrix from a config file. Example config:
Run it with:
target uses the same shared server shape as the rest of the CLI, so it can point at either:
  • an HTTP target with url
  • a stdio target with command, optional args, optional env, and optional cwd

Categories and check ids

Two categories are available:
  • tools
  • resources
Specific check ids:
  • ui-tools-present
  • ui-tool-metadata-valid
  • ui-tool-input-schema-valid
  • ui-listed-resources-valid
  • ui-resources-readable
  • ui-resource-contents-valid
  • ui-resource-meta-valid
Use --category to run a subset by category, or --check-id to run specific checks.
If you pass both --category and --check-id, the explicit --check-id selection wins.

Render one app result

Use tools list to find UI-capable tools. A tool has interactive UI when its metadata includes _meta.ui.resourceUri, deprecated _meta["ui/resourceUri"], or openai/outputTemplate in toolsMetadata. Then use tools call --ui when you need to inspect the output of one UI-capable tool call. The CLI starts or attaches to the local Inspector backend, opens Inspector by default in a TTY, connects the target server, focuses App Builder in the active Inspector browser client, injects the already-completed tool result, and requests a UI snapshot. --inspector-url is the Inspector backend/API URL; --frontend-url is the browser/client URL and skips frontend discovery. Use --no-open when browser automation already opened http://127.0.0.1:6274/#app-builder; use --attach-only to disallow startup, browser opening, and discovery. Default non-TTY --ui runs do not open a browser unless --open is passed. TTY stderr runs print the App Builder URL and initial wait progress unless --quiet is set; the elapsed-seconds heartbeat only appears when stderr is a TTY.
tools call --ui executes the server tool once. Inspector receives the completed result through renderToolResult, so the UI render path does not call the tool a second time. The rendered state lives in the active Inspector client; opening the URL in a fresh tab later does not hydrate the old render.
If inspectorRender.status is skipped, the tool call succeeded but Inspector had no active browser client, a render precondition was missing, or the render wait timed out; the envelope includes a stable root warning and inspectorRender.warning with one of the stable codes no_active_client, timeout, disconnected_server, or unsupported_in_mode, plus an inspectorRender.remediation hint. Non-quiet runs print a stderr tip, and the command keeps the tool-call exit code unless --require-render is set. If the command returns success: false with an error, check whether the failure came from result or from an Inspector render command. Raw ui:// resource HTML can be inspected directly:

Shared connection flags

apps conformance uses the same shared target flags as the rest of the CLI: Stdio child processes inherit the parent shell environment by default. Use -e/--env to add values or override inherited ones when the app server needs project-specific configuration. --transport is optional; without it, --url implies HTTP and --command implies stdio.

Outcomes: passed, failed, incomplete

A run reports one of three outcomes, and passed is true only for the first: Every skipped check carries a skipReason:
  • not-applicable — the check cannot apply here (no MCP Apps tools discovered, no UI resources listed). These never hold a run back.
  • could-not-run — the check applies but was never exercised (tools/list did not complete, connection failed). These make the run incomplete.
An incomplete run carries a root incompleteReason naming the checks that did not run and what to change; non-quiet runs also print it to stderr. For a suite, the exit code is the worst of all runs (failure outranks incomplete).

Notes

  • The runner always advertises the MCP Apps UI extension capability so servers do not hide their MCP Apps surface when custom client capabilities are provided.
  • Deprecated _meta["ui/resourceUri"] is accepted but reported as a warning.
  • Tool name conventions (length, character set, uniqueness) are core MCP rules, so this suite does not check them. The protocol suite reports them on every tool as the advice item readiness-tool-name-conventions, on protocol 2025-11-25 and later.
  • permissions are validated against the SEP-1865 object shape, for example geolocation: {} instead of booleans.
  • Exit codes are CI-friendly: 0 when all selected checks pass, 1 when any check fails, 2 for invalid command usage or config files, and 3 when the run is incomplete because a selected check could not be run.